Home | History | Annotate | Download | only in profiler
      1 /*
      2  * Copyright (C) 2011 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  * @implements {WebInspector.ProfileType.DataDisplayDelegate}
     34  * @extends {WebInspector.VBox}
     35  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
     36  * @param {!WebInspector.HeapProfileHeader} profile
     37  */
     38 WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
     39 {
     40     WebInspector.VBox.call(this);
     41 
     42     this.element.classList.add("heap-snapshot-view");
     43 
     44     profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
     45     profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
     46 
     47     if (profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
     48         this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
     49         this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
     50     }
     51 
     52     this._parentDataDisplayDelegate = dataDisplayDelegate;
     53 
     54     this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
     55     this._splitView.show(this.element);
     56 
     57     this._containmentView = new WebInspector.VBox();
     58     this._containmentView.setMinimumSize(50, 25);
     59     this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(this);
     60     this._containmentDataGrid.show(this._containmentView.element);
     61     this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
     62 
     63     this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
     64 
     65     this._constructorsView = new WebInspector.VBox();
     66     this._constructorsView.setMinimumSize(50, 25);
     67 
     68     this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(this);
     69     this._constructorsDataGrid.show(this._constructorsView.element);
     70     this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
     71 
     72     this._diffView = new WebInspector.VBox();
     73     this._diffView.setMinimumSize(50, 25);
     74 
     75     this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(this);
     76     this._diffDataGrid.show(this._diffView.element);
     77     this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
     78 
     79     if (profile._hasAllocationStacks) {
     80         this._allocationView = new WebInspector.VBox();
     81         this._allocationView.setMinimumSize(50, 25);
     82         this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.target() , this);
     83         this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
     84         this._allocationDataGrid.show(this._allocationView.element);
     85 
     86         this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.target());
     87         this._allocationStackView.setMinimumSize(50, 25);
     88 
     89         this._tabbedPane = new WebInspector.TabbedPane();
     90         this._tabbedPane.closeableTabs = false;
     91         this._tabbedPane.headerElement().classList.add("heap-object-details-header");
     92     }
     93 
     94     this._retainmentView = new WebInspector.VBox();
     95     this._retainmentView.setMinimumSize(50, 21);
     96     this._retainmentView.element.classList.add("retaining-paths-view");
     97 
     98     var splitViewResizer;
     99     if (this._allocationStackView) {
    100         this._tabbedPane = new WebInspector.TabbedPane();
    101         this._tabbedPane.closeableTabs = false;
    102         this._tabbedPane.headerElement().classList.add("heap-object-details-header");
    103 
    104         this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
    105         this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
    106 
    107         splitViewResizer = this._tabbedPane.headerElement();
    108         this._objectDetailsView = this._tabbedPane;
    109     } else {
    110         var retainmentViewHeader = document.createElementWithClass("div", "heap-snapshot-view-resizer");
    111         var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
    112         var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
    113         retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
    114         this._retainmentView.element.appendChild(retainmentViewHeader);
    115 
    116         splitViewResizer = retainmentViewHeader;
    117         this._objectDetailsView = this._retainmentView;
    118     }
    119     this._splitView.hideDefaultResizer();
    120     this._splitView.installResizer(splitViewResizer);
    121 
    122     this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(this);
    123     this._retainmentDataGrid.show(this._retainmentView.element);
    124     this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
    125     this._retainmentDataGrid.reset();
    126 
    127     this._perspectives = [];
    128     this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
    129     if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
    130         this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
    131     this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
    132     if (this._allocationView)
    133         this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
    134     this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
    135 
    136     this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
    137     for (var i = 0; i < this._perspectives.length; ++i)
    138         this._perspectiveSelect.createOption(this._perspectives[i].title());
    139 
    140     this._profile = profile;
    141 
    142     this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
    143     this._baseSelect.visible = false;
    144     this._updateBaseOptions();
    145 
    146     this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
    147     this._filterSelect.visible = false;
    148     this._updateFilterOptions();
    149 
    150     this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
    151     this._classNameFilter.visible = false;
    152     this._constructorsDataGrid.setNameFilter(this._classNameFilter);
    153     this._diffDataGrid.setNameFilter(this._classNameFilter);
    154 
    155     this._selectedSizeText = new WebInspector.StatusBarText("");
    156 
    157     this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
    158 
    159     this._currentPerspectiveIndex = 0;
    160     this._currentPerspective = this._perspectives[0];
    161     this._currentPerspective.activate(this);
    162     this._dataGrid = this._currentPerspective.masterGrid(this);
    163 
    164     this._refreshView();
    165 }
    166 
    167 /**
    168  * @constructor
    169  * @param {string} title
    170  */
    171 WebInspector.HeapSnapshotView.Perspective = function(title)
    172 {
    173     this._title = title;
    174 }
    175 
    176 WebInspector.HeapSnapshotView.Perspective.prototype = {
    177     /**
    178      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    179      */
    180     activate: function(heapSnapshotView) { },
    181 
    182     /**
    183      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    184      */
    185     deactivate: function(heapSnapshotView)
    186     {
    187         heapSnapshotView._baseSelect.visible = false;
    188         heapSnapshotView._filterSelect.visible = false;
    189         heapSnapshotView._classNameFilter.visible = false;
    190         if (heapSnapshotView._trackingOverviewGrid)
    191             heapSnapshotView._trackingOverviewGrid.detach();
    192         if (heapSnapshotView._allocationView)
    193             heapSnapshotView._allocationView.detach();
    194         if (heapSnapshotView._statisticsView)
    195             heapSnapshotView._statisticsView.detach();
    196 
    197         heapSnapshotView._splitView.detach();
    198         heapSnapshotView._splitView.detachChildViews();
    199     },
    200 
    201     /**
    202      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    203      * @return {?WebInspector.DataGrid}
    204      */
    205     masterGrid: function(heapSnapshotView)
    206     {
    207         return null;
    208     },
    209 
    210     /**
    211      * @return {string}
    212      */
    213     title: function()
    214     {
    215         return this._title;
    216     },
    217 
    218     /**
    219      * @return {boolean}
    220      */
    221     supportsSearch: function()
    222     {
    223         return false;
    224     }
    225 }
    226 
    227 /**
    228  * @constructor
    229  * @extends {WebInspector.HeapSnapshotView.Perspective}
    230  */
    231 WebInspector.HeapSnapshotView.SummaryPerspective = function()
    232 {
    233     WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Summary"));
    234 }
    235 
    236 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
    237     /**
    238      * @override
    239      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    240      */
    241     activate: function(heapSnapshotView)
    242     {
    243         heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
    244         heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
    245         heapSnapshotView._splitView.show(heapSnapshotView.element);
    246         heapSnapshotView._filterSelect.visible = true;
    247         heapSnapshotView._classNameFilter.visible = true;
    248         if (heapSnapshotView._trackingOverviewGrid) {
    249             heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
    250             heapSnapshotView._trackingOverviewGrid.update();
    251             heapSnapshotView._trackingOverviewGrid._updateGrid();
    252         }
    253     },
    254 
    255     /**
    256      * @override
    257      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    258      * @return {?WebInspector.DataGrid}
    259      */
    260     masterGrid: function(heapSnapshotView)
    261     {
    262         return heapSnapshotView._constructorsDataGrid;
    263     },
    264 
    265     /**
    266      * @override
    267      * @return {boolean}
    268      */
    269     supportsSearch: function()
    270     {
    271         return true;
    272     },
    273 
    274    __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
    275 }
    276 
    277 /**
    278  * @constructor
    279  * @extends {WebInspector.HeapSnapshotView.Perspective}
    280  */
    281 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
    282 {
    283     WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Comparison"));
    284 }
    285 
    286 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
    287     /**
    288      * @override
    289      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    290      */
    291     activate: function(heapSnapshotView)
    292     {
    293         heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
    294         heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
    295         heapSnapshotView._splitView.show(heapSnapshotView.element);
    296         heapSnapshotView._baseSelect.visible = true;
    297         heapSnapshotView._classNameFilter.visible = true;
    298     },
    299 
    300     /**
    301      * @override
    302      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    303      * @return {?WebInspector.DataGrid}
    304      */
    305     masterGrid: function(heapSnapshotView)
    306     {
    307         return heapSnapshotView._diffDataGrid;
    308     },
    309 
    310     /**
    311      * @override
    312      * @return {boolean}
    313      */
    314     supportsSearch: function()
    315     {
    316         return true;
    317     },
    318 
    319    __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
    320 }
    321 
    322 /**
    323  * @constructor
    324  * @extends {WebInspector.HeapSnapshotView.Perspective}
    325  */
    326 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
    327 {
    328     WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Containment"));
    329 }
    330 
    331 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
    332     /**
    333      * @override
    334      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    335      */
    336     activate: function(heapSnapshotView)
    337     {
    338         heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
    339         heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
    340         heapSnapshotView._splitView.show(heapSnapshotView.element);
    341     },
    342 
    343     /**
    344      * @override
    345      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    346      * @return {?WebInspector.DataGrid}
    347      */
    348     masterGrid: function(heapSnapshotView)
    349     {
    350         return heapSnapshotView._containmentDataGrid;
    351     },
    352    __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
    353 }
    354 
    355 /**
    356  * @constructor
    357  * @extends {WebInspector.HeapSnapshotView.Perspective}
    358  */
    359 WebInspector.HeapSnapshotView.AllocationPerspective = function()
    360 {
    361     WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Allocation"));
    362     this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
    363 
    364     var resizer = document.createElementWithClass("div", "heap-snapshot-view-resizer");
    365     var title = resizer.createChild("div", "title").createChild("span");
    366     title.textContent = WebInspector.UIString("Live objects");
    367     this._allocationSplitView.hideDefaultResizer();
    368     this._allocationSplitView.installResizer(resizer);
    369 
    370     this._allocationSplitView.sidebarElement().appendChild(resizer);
    371 }
    372 
    373 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
    374     /**
    375      * @override
    376      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    377      */
    378     activate: function(heapSnapshotView)
    379     {
    380         heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
    381         heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
    382         heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
    383         heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
    384         this._allocationSplitView.show(heapSnapshotView.element);
    385 
    386         heapSnapshotView._constructorsDataGrid.clear();
    387         var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
    388         if (selectedNode)
    389             heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
    390     },
    391 
    392     /**
    393      * @override
    394      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    395      */
    396     deactivate: function(heapSnapshotView)
    397     {
    398         this._allocationSplitView.detach();
    399         WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
    400     },
    401 
    402     /**
    403      * @override
    404      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    405      * @return {?WebInspector.DataGrid}
    406      */
    407     masterGrid: function(heapSnapshotView)
    408     {
    409         return heapSnapshotView._allocationDataGrid;
    410     },
    411 
    412    __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
    413 }
    414 
    415 /**
    416  * @constructor
    417  * @extends {WebInspector.HeapSnapshotView.Perspective}
    418  */
    419 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
    420 {
    421     WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Statistics"));
    422 }
    423 
    424 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
    425     /**
    426      * @override
    427      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    428      */
    429     activate: function(heapSnapshotView)
    430     {
    431         heapSnapshotView._statisticsView.show(heapSnapshotView.element);
    432     },
    433 
    434     /**
    435      * @override
    436      * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
    437      * @return {?WebInspector.DataGrid}
    438      */
    439     masterGrid: function(heapSnapshotView)
    440     {
    441         return null;
    442     },
    443 
    444    __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
    445 }
    446 
    447 
    448 WebInspector.HeapSnapshotView.prototype = {
    449     /**
    450      * @override
    451      * @param {?WebInspector.ProfileHeader} profile
    452      * @return {?WebInspector.View}
    453      */
    454     showProfile: function(profile)
    455     {
    456         return this._parentDataDisplayDelegate.showProfile(profile);
    457     },
    458 
    459     /**
    460      * @override
    461      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
    462      * @param {string} perspectiveName
    463      */
    464     showObject: function(snapshotObjectId, perspectiveName)
    465     {
    466         if (snapshotObjectId <= this._profile.maxJSObjectId)
    467             this.highlightLiveObject(perspectiveName, snapshotObjectId);
    468         else
    469             this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName);
    470     },
    471 
    472     _refreshView: function()
    473     {
    474         this._profile.load(profileCallback.bind(this));
    475 
    476         /**
    477          * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
    478          * @this {WebInspector.HeapSnapshotView}
    479          */
    480         function profileCallback(heapSnapshotProxy)
    481         {
    482             heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
    483             var list = this._profiles();
    484             var profileIndex = list.indexOf(this._profile);
    485             this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
    486             this._dataGrid.setDataSource(heapSnapshotProxy);
    487             if (this._trackingOverviewGrid)
    488                 this._trackingOverviewGrid._updateGrid();
    489         }
    490     },
    491 
    492     /**
    493      * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
    494      */
    495     _gotStatistics: function(statistics)
    496     {
    497         this._statisticsView.setTotal(statistics.total);
    498         this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
    499         this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
    500         this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
    501         this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
    502         this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
    503     },
    504 
    505     _onIdsRangeChanged: function(event)
    506     {
    507         var minId = event.data.minId;
    508         var maxId = event.data.maxId;
    509         this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
    510         if (this._constructorsDataGrid.snapshot)
    511             this._constructorsDataGrid.setSelectionRange(minId, maxId);
    512     },
    513 
    514     get statusBarItems()
    515     {
    516         var result = [this._perspectiveSelect.element, this._classNameFilter.element];
    517         if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
    518             result.push(this._baseSelect.element, this._filterSelect.element);
    519         result.push(this._selectedSizeText.element);
    520         return result;
    521     },
    522 
    523     wasShown: function()
    524     {
    525         // FIXME: load base and current snapshots in parallel
    526         this._profile.load(profileCallback.bind(this));
    527 
    528         /**
    529          * @this {WebInspector.HeapSnapshotView}
    530          */
    531         function profileCallback() {
    532             this._profile._wasShown();
    533             if (this._baseProfile)
    534                 this._baseProfile.load(function() { });
    535         }
    536     },
    537 
    538     willHide: function()
    539     {
    540         this._currentSearchResultIndex = -1;
    541         this._popoverHelper.hidePopover();
    542         if (this.helpPopover && this.helpPopover.isShowing())
    543             this.helpPopover.hide();
    544     },
    545 
    546     searchCanceled: function()
    547     {
    548         if (this._searchResults) {
    549             for (var i = 0; i < this._searchResults.length; ++i) {
    550                 var node = this._searchResults[i].node;
    551                 delete node._searchMatched;
    552                 node.refresh();
    553             }
    554         }
    555 
    556         delete this._searchFinishedCallback;
    557         this._currentSearchResultIndex = -1;
    558         this._searchResults = [];
    559     },
    560 
    561     /**
    562      * @param {string} query
    563      * @param {function(!WebInspector.View, number)} finishedCallback
    564      */
    565     performSearch: function(query, finishedCallback)
    566     {
    567         // Call searchCanceled since it will reset everything we need before doing a new search.
    568         this.searchCanceled();
    569 
    570         query = query.trim();
    571 
    572         if (!query)
    573             return;
    574         if (!this._currentPerspective.supportsSearch())
    575             return;
    576 
    577         /**
    578          * @param {boolean} found
    579          * @this {WebInspector.HeapSnapshotView}
    580          */
    581         function didHighlight(found)
    582         {
    583             finishedCallback(this, found ? 1 : 0);
    584         }
    585 
    586         if (query.charAt(0) === "@") {
    587             var snapshotNodeId = parseInt(query.substring(1), 10);
    588             if (!isNaN(snapshotNodeId))
    589                 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
    590             else
    591                 finishedCallback(this, 0);
    592             return;
    593         }
    594 
    595         this._searchFinishedCallback = finishedCallback;
    596         var nameRegExp = createPlainTextSearchRegex(query, "i");
    597 
    598         function matchesByName(gridNode) {
    599             return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
    600         }
    601 
    602         function matchesQuery(gridNode)
    603         {
    604             delete gridNode._searchMatched;
    605             if (matchesByName(gridNode)) {
    606                 gridNode._searchMatched = true;
    607                 gridNode.refresh();
    608                 return true;
    609             }
    610             return false;
    611         }
    612 
    613         var current = this._dataGrid.rootNode().children[0];
    614         var depth = 0;
    615         var info = {};
    616 
    617         // Restrict to type nodes and instances.
    618         const maxDepth = 1;
    619 
    620         while (current) {
    621             if (matchesQuery(current))
    622                 this._searchResults.push({ node: current });
    623             current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
    624             depth += info.depthChange;
    625         }
    626 
    627         finishedCallback(this, this._searchResults.length);
    628     },
    629 
    630     jumpToFirstSearchResult: function()
    631     {
    632         if (!this._searchResults || !this._searchResults.length)
    633             return;
    634         this._currentSearchResultIndex = 0;
    635         this._jumpToSearchResult(this._currentSearchResultIndex);
    636     },
    637 
    638     jumpToLastSearchResult: function()
    639     {
    640         if (!this._searchResults || !this._searchResults.length)
    641             return;
    642         this._currentSearchResultIndex = (this._searchResults.length - 1);
    643         this._jumpToSearchResult(this._currentSearchResultIndex);
    644     },
    645 
    646     jumpToNextSearchResult: function()
    647     {
    648         if (!this._searchResults || !this._searchResults.length)
    649             return;
    650         if (++this._currentSearchResultIndex >= this._searchResults.length)
    651             this._currentSearchResultIndex = 0;
    652         this._jumpToSearchResult(this._currentSearchResultIndex);
    653     },
    654 
    655     jumpToPreviousSearchResult: function()
    656     {
    657         if (!this._searchResults || !this._searchResults.length)
    658             return;
    659         if (--this._currentSearchResultIndex < 0)
    660             this._currentSearchResultIndex = (this._searchResults.length - 1);
    661         this._jumpToSearchResult(this._currentSearchResultIndex);
    662     },
    663 
    664     /**
    665      * @return {boolean}
    666      */
    667     showingFirstSearchResult: function()
    668     {
    669         return (this._currentSearchResultIndex === 0);
    670     },
    671 
    672     /**
    673      * @return {boolean}
    674      */
    675     showingLastSearchResult: function()
    676     {
    677         return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
    678     },
    679 
    680     /**
    681      * @return {number}
    682      */
    683     currentSearchResultIndex: function() {
    684         return this._currentSearchResultIndex;
    685     },
    686 
    687     _jumpToSearchResult: function(index)
    688     {
    689         var searchResult = this._searchResults[index];
    690         if (!searchResult)
    691             return;
    692 
    693         var node = searchResult.node;
    694         node.revealAndSelect();
    695     },
    696 
    697     refreshVisibleData: function()
    698     {
    699         if (!this._dataGrid)
    700             return;
    701         var child = this._dataGrid.rootNode().children[0];
    702         while (child) {
    703             child.refresh();
    704             child = child.traverseNextNode(false, null, true);
    705         }
    706     },
    707 
    708     _changeBase: function()
    709     {
    710         if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
    711             return;
    712 
    713         this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
    714         var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
    715         // Change set base data source only if main data source is already set.
    716         if (dataGrid.snapshot)
    717             this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
    718 
    719         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
    720             return;
    721 
    722         // The current search needs to be performed again. First negate out previous match
    723         // count by calling the search finished callback with a negative number of matches.
    724         // Then perform the search again with the same query and callback.
    725         this._searchFinishedCallback(this, -this._searchResults.length);
    726         this.performSearch(this.currentQuery, this._searchFinishedCallback);
    727     },
    728 
    729     _changeFilter: function()
    730     {
    731         var profileIndex = this._filterSelect.selectedIndex() - 1;
    732         this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
    733 
    734         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
    735             action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
    736             label: this._filterSelect.selectedOption().label
    737         });
    738 
    739         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
    740             return;
    741 
    742         // The current search needs to be performed again. First negate out previous match
    743         // count by calling the search finished callback with a negative number of matches.
    744         // Then perform the search again with the same query and callback.
    745         this._searchFinishedCallback(this, -this._searchResults.length);
    746         this.performSearch(this.currentQuery, this._searchFinishedCallback);
    747     },
    748 
    749     /**
    750      * @return {!Array.<!WebInspector.ProfileHeader>}
    751      */
    752     _profiles: function()
    753     {
    754         return this._profile.profileType().getProfiles();
    755     },
    756 
    757     /**
    758      * @param {!WebInspector.ContextMenu} contextMenu
    759      * @param {!Event} event
    760      */
    761     populateContextMenu: function(contextMenu, event)
    762     {
    763         if (this._dataGrid)
    764             this._dataGrid.populateContextMenu(contextMenu, event);
    765     },
    766 
    767     _selectionChanged: function(event)
    768     {
    769         var selectedNode = event.target.selectedNode;
    770         this._setSelectedNodeForDetailsView(selectedNode);
    771         this._inspectedObjectChanged(event);
    772     },
    773 
    774     _onSelectAllocationNode: function(event)
    775     {
    776         var selectedNode = event.target.selectedNode;
    777         this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
    778         this._setSelectedNodeForDetailsView(null);
    779     },
    780 
    781     _inspectedObjectChanged: function(event)
    782     {
    783         var selectedNode = event.target.selectedNode;
    784         var target = this._profile.target();
    785         if (target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
    786             target.consoleAgent().addInspectedHeapObject(selectedNode.snapshotNodeId);
    787     },
    788 
    789     /**
    790      * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
    791      */
    792     _setSelectedNodeForDetailsView: function(nodeItem)
    793     {
    794         var dataSource = nodeItem && nodeItem.retainersDataSource();
    795         if (dataSource) {
    796             this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
    797             if (this._allocationStackView)
    798                 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex)
    799         } else {
    800             if (this._allocationStackView)
    801                 this._allocationStackView.clear();
    802             this._retainmentDataGrid.reset();
    803         }
    804     },
    805 
    806     /**
    807      * @param {string} perspectiveTitle
    808      * @param {function()} callback
    809      */
    810     _changePerspectiveAndWait: function(perspectiveTitle, callback)
    811     {
    812         var perspectiveIndex = null;
    813         for (var i = 0; i < this._perspectives.length; ++i) {
    814             if (this._perspectives[i].title() === perspectiveTitle) {
    815                 perspectiveIndex = i;
    816                 break;
    817             }
    818         }
    819         if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
    820             setTimeout(callback, 0);
    821             return;
    822         }
    823 
    824         /**
    825          * @this {WebInspector.HeapSnapshotView}
    826          */
    827         function dataGridContentShown(event)
    828         {
    829             var dataGrid = event.data;
    830             dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
    831             if (dataGrid === this._dataGrid)
    832                 callback();
    833         }
    834         this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
    835 
    836         this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
    837         this._changePerspective(perspectiveIndex);
    838     },
    839 
    840     _updateDataSourceAndView: function()
    841     {
    842         var dataGrid = this._dataGrid;
    843         if (!dataGrid || dataGrid.snapshot)
    844             return;
    845 
    846         this._profile.load(didLoadSnapshot.bind(this));
    847 
    848         /**
    849          * @this {WebInspector.HeapSnapshotView}
    850          */
    851         function didLoadSnapshot(snapshotProxy)
    852         {
    853             if (this._dataGrid !== dataGrid)
    854                 return;
    855             if (dataGrid.snapshot !== snapshotProxy)
    856                 dataGrid.setDataSource(snapshotProxy);
    857             if (dataGrid === this._diffDataGrid) {
    858                 if (!this._baseProfile)
    859                     this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
    860                 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
    861             }
    862         }
    863 
    864         /**
    865          * @this {WebInspector.HeapSnapshotView}
    866          */
    867         function didLoadBaseSnaphot(baseSnapshotProxy)
    868         {
    869             if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
    870                 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
    871         }
    872     },
    873 
    874     _onSelectedPerspectiveChanged: function(event)
    875     {
    876         this._changePerspective(event.target.selectedIndex);
    877         // FIXME: This is needed by CodeSchool extension.
    878         this._onSelectedViewChanged(event);
    879     },
    880 
    881     _onSelectedViewChanged: function(event)
    882     {
    883     },
    884 
    885     /**
    886      * @param {number} selectedIndex
    887      */
    888     _changePerspective: function(selectedIndex)
    889     {
    890         if (selectedIndex === this._currentPerspectiveIndex)
    891             return;
    892 
    893         this._currentPerspectiveIndex = selectedIndex;
    894 
    895         this._currentPerspective.deactivate(this);
    896         var perspective = this._perspectives[selectedIndex];
    897         this._currentPerspective = perspective;
    898         this._dataGrid = perspective.masterGrid(this);
    899         perspective.activate(this);
    900 
    901         this.refreshVisibleData();
    902         if (this._dataGrid)
    903             this._dataGrid.updateWidths();
    904 
    905         this._updateDataSourceAndView();
    906 
    907         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
    908             return;
    909 
    910         // The current search needs to be performed again. First negate out previous match
    911         // count by calling the search finished callback with a negative number of matches.
    912         // Then perform the search again the with same query and callback.
    913         this._searchFinishedCallback(this, -this._searchResults.length);
    914         this.performSearch(this.currentQuery, this._searchFinishedCallback);
    915     },
    916 
    917     /**
    918      * @param {string} perspectiveName
    919      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
    920      */
    921     highlightLiveObject: function(perspectiveName, snapshotObjectId)
    922     {
    923         this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
    924 
    925         /**
    926          * @this {WebInspector.HeapSnapshotView}
    927          */
    928         function didChangePerspective()
    929         {
    930             this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
    931         }
    932 
    933         function didHighlightObject(found)
    934         {
    935             if (!found)
    936                 WebInspector.console.error("Cannot find corresponding heap snapshot node");
    937         }
    938     },
    939 
    940     _getHoverAnchor: function(target)
    941     {
    942         var span = target.enclosingNodeOrSelfWithNodeName("span");
    943         if (!span)
    944             return;
    945         var row = target.enclosingNodeOrSelfWithNodeName("tr");
    946         if (!row)
    947             return;
    948         span.node = row._dataGridNode;
    949         return span;
    950     },
    951 
    952     _resolveObjectForPopover: function(element, showCallback, objectGroupName)
    953     {
    954         if (!this._profile.target())
    955             return;
    956         element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName);
    957     },
    958 
    959     _updateBaseOptions: function()
    960     {
    961         var list = this._profiles();
    962         // We're assuming that snapshots can only be added.
    963         if (this._baseSelect.size() === list.length)
    964             return;
    965 
    966         for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
    967             var title = list[i].title;
    968             this._baseSelect.createOption(title);
    969         }
    970     },
    971 
    972     _updateFilterOptions: function()
    973     {
    974         var list = this._profiles();
    975         // We're assuming that snapshots can only be added.
    976         if (this._filterSelect.size() - 1 === list.length)
    977             return;
    978 
    979         if (!this._filterSelect.size())
    980             this._filterSelect.createOption(WebInspector.UIString("All objects"));
    981 
    982         for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
    983             var title = list[i].title;
    984             if (!i)
    985                 title = WebInspector.UIString("Objects allocated before %s", title);
    986             else
    987                 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
    988             this._filterSelect.createOption(title);
    989         }
    990     },
    991 
    992     _updateControls: function()
    993     {
    994         this._updateBaseOptions();
    995         this._updateFilterOptions();
    996     },
    997 
    998     /**
    999      * @param {!WebInspector.Event} event
   1000      */
   1001     _onReceiveSnapshot: function(event)
   1002     {
   1003         this._updateControls();
   1004     },
   1005 
   1006     /**
   1007      * @param {!WebInspector.Event} event
   1008      */
   1009     _onProfileHeaderRemoved: function(event)
   1010     {
   1011         var profile = event.data;
   1012         if (this._profile === profile) {
   1013             this.detach();
   1014             this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
   1015             this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
   1016             this.dispose();
   1017         } else {
   1018             this._updateControls();
   1019         }
   1020     },
   1021 
   1022     dispose: function()
   1023     {
   1024         if (this._allocationStackView) {
   1025             this._allocationStackView.clear();
   1026             this._allocationDataGrid.dispose();
   1027         }
   1028     },
   1029 
   1030     __proto__: WebInspector.VBox.prototype
   1031 }
   1032 
   1033 /**
   1034  * @constructor
   1035  * @extends {WebInspector.ProfileType}
   1036  * @implements {WebInspector.TargetManager.Observer}
   1037  * @param {string=} id
   1038  * @param {string=} title
   1039  */
   1040 WebInspector.HeapSnapshotProfileType = function(id, title)
   1041 {
   1042     WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
   1043     WebInspector.targetManager.observeTargets(this);
   1044     WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
   1045     WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this);
   1046     WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this);
   1047 }
   1048 
   1049 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
   1050 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
   1051 
   1052 WebInspector.HeapSnapshotProfileType.prototype = {
   1053     /**
   1054      * @param {!WebInspector.Target} target
   1055      */
   1056     targetAdded: function(target)
   1057     {
   1058         target.heapProfilerModel.enable();
   1059     },
   1060 
   1061     /**
   1062      * @param {!WebInspector.Target} target
   1063      */
   1064     targetRemoved: function(target)
   1065     {
   1066     },
   1067 
   1068     /**
   1069      * @override
   1070      * @return {string}
   1071      */
   1072     fileExtension: function()
   1073     {
   1074         return ".heapsnapshot";
   1075     },
   1076 
   1077     get buttonTooltip()
   1078     {
   1079         return WebInspector.UIString("Take heap snapshot.");
   1080     },
   1081 
   1082     /**
   1083      * @override
   1084      * @return {boolean}
   1085      */
   1086     isInstantProfile: function()
   1087     {
   1088         return true;
   1089     },
   1090 
   1091     /**
   1092      * @override
   1093      * @return {boolean}
   1094      */
   1095     buttonClicked: function()
   1096     {
   1097         this._takeHeapSnapshot(function() {});
   1098         WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
   1099         return false;
   1100     },
   1101 
   1102     get treeItemTitle()
   1103     {
   1104         return WebInspector.UIString("HEAP SNAPSHOTS");
   1105     },
   1106 
   1107     get description()
   1108     {
   1109         return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
   1110     },
   1111 
   1112     /**
   1113      * @override
   1114      * @param {string} title
   1115      * @return {!WebInspector.ProfileHeader}
   1116      */
   1117     createProfileLoadedFromFile: function(title)
   1118     {
   1119         return new WebInspector.HeapProfileHeader(null, this, title);
   1120     },
   1121 
   1122     _takeHeapSnapshot: function(callback)
   1123     {
   1124         if (this.profileBeingRecorded())
   1125             return;
   1126         var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target));
   1127         var profile = new WebInspector.HeapProfileHeader(target, this);
   1128         this.setProfileBeingRecorded(profile);
   1129         this.addProfile(profile);
   1130         profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
   1131 
   1132         /**
   1133          * @param {?string} error
   1134          * @this {WebInspector.HeapSnapshotProfileType}
   1135          */
   1136         function didTakeHeapSnapshot(error)
   1137         {
   1138             var profile = this._profileBeingRecorded;
   1139             profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
   1140             profile._finishLoad();
   1141             this.setProfileBeingRecorded(null);
   1142             this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
   1143             callback();
   1144         }
   1145         target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
   1146     },
   1147 
   1148     /**
   1149      * @param {!WebInspector.Event} event
   1150      */
   1151     _addHeapSnapshotChunk: function(event)
   1152     {
   1153         if (!this.profileBeingRecorded())
   1154             return;
   1155         var chunk = /** @type {string} */(event.data);
   1156         this.profileBeingRecorded().transferChunk(chunk);
   1157     },
   1158 
   1159     /**
   1160      * @param {!WebInspector.Event} event
   1161      */
   1162     _reportHeapSnapshotProgress: function(event)
   1163     {
   1164         var profile = this.profileBeingRecorded();
   1165         if (!profile)
   1166             return;
   1167         var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data);
   1168         profile.updateStatus(WebInspector.UIString("%.0f%", (data.done / data.total) * 100), true);
   1169         if (data.finished)
   1170             profile._prepareToLoad();
   1171     },
   1172 
   1173     _resetProfiles: function()
   1174     {
   1175         this._reset();
   1176     },
   1177 
   1178     _snapshotReceived: function(profile)
   1179     {
   1180         if (this._profileBeingRecorded === profile)
   1181             this.setProfileBeingRecorded(null);
   1182         this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
   1183     },
   1184 
   1185     __proto__: WebInspector.ProfileType.prototype
   1186 }
   1187 
   1188 
   1189 /**
   1190  * @constructor
   1191  * @extends {WebInspector.HeapSnapshotProfileType}
   1192  */
   1193 WebInspector.TrackingHeapSnapshotProfileType = function()
   1194 {
   1195     WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
   1196 }
   1197 
   1198 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
   1199 
   1200 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
   1201 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
   1202 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
   1203 
   1204 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
   1205 
   1206     /**
   1207      * @param {!WebInspector.Target} target
   1208      */
   1209     targetAdded: function(target)
   1210     {
   1211         WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target);
   1212         target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
   1213         target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
   1214     },
   1215 
   1216     /**
   1217      * @param {!WebInspector.Target} target
   1218      */
   1219     targetRemoved: function(target)
   1220     {
   1221         WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target);
   1222         target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
   1223         target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
   1224     },
   1225 
   1226     /**
   1227      * @param {!WebInspector.Event} event
   1228      */
   1229     _heapStatsUpdate: function(event)
   1230     {
   1231         if (!this._profileSamples)
   1232             return;
   1233         var samples = /** @type {!Array.<number>} */ (event.data);
   1234         var index;
   1235         for (var i = 0; i < samples.length; i += 3) {
   1236             index = samples[i];
   1237             var count = samples[i+1];
   1238             var size  = samples[i+2];
   1239             this._profileSamples.sizes[index] = size;
   1240             if (!this._profileSamples.max[index])
   1241                 this._profileSamples.max[index] = size;
   1242         }
   1243     },
   1244 
   1245     /**
   1246      * @param {!WebInspector.Event} event
   1247      */
   1248     _lastSeenObjectId: function(event)
   1249     {
   1250         var profileSamples = this._profileSamples;
   1251         if (!profileSamples)
   1252             return;
   1253         var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data);
   1254         var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
   1255         profileSamples.ids[currentIndex] = data.lastSeenObjectId;
   1256         if (!profileSamples.max[currentIndex]) {
   1257             profileSamples.max[currentIndex] = 0;
   1258             profileSamples.sizes[currentIndex] = 0;
   1259         }
   1260         profileSamples.timestamps[currentIndex] = data.timestamp;
   1261         if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0])
   1262             profileSamples.totalTime *= 2;
   1263         this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
   1264         this._profileBeingRecorded.updateStatus(null, true);
   1265     },
   1266 
   1267     /**
   1268      * @override
   1269      * @return {boolean}
   1270      */
   1271     hasTemporaryView: function()
   1272     {
   1273         return true;
   1274     },
   1275 
   1276     get buttonTooltip()
   1277     {
   1278         return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
   1279     },
   1280 
   1281     /**
   1282      * @override
   1283      * @return {boolean}
   1284      */
   1285     isInstantProfile: function()
   1286     {
   1287         return false;
   1288     },
   1289 
   1290     /**
   1291      * @override
   1292      * @return {boolean}
   1293      */
   1294     buttonClicked: function()
   1295     {
   1296         return this._toggleRecording();
   1297     },
   1298 
   1299     _startRecordingProfile: function()
   1300     {
   1301         if (this.profileBeingRecorded())
   1302             return;
   1303         var recordAllocationStacks = WebInspector.settings.recordAllocationStacks.get();
   1304         this._addNewProfile(recordAllocationStacks);
   1305         this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks);
   1306     },
   1307 
   1308     /**
   1309      * @param {boolean} withAllocationStacks
   1310      */
   1311     _addNewProfile: function(withAllocationStacks)
   1312     {
   1313         var target =  WebInspector.context.flavor(WebInspector.Target);
   1314         this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined, withAllocationStacks));
   1315         this._lastSeenIndex = -1;
   1316         this._profileSamples = {
   1317             'sizes': [],
   1318             'ids': [],
   1319             'timestamps': [],
   1320             'max': [],
   1321             'totalTime': 30000
   1322         };
   1323         this._profileBeingRecorded._profileSamples = this._profileSamples;
   1324         this._recording = true;
   1325         this.addProfile(this._profileBeingRecorded);
   1326         this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
   1327         this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
   1328     },
   1329 
   1330     _stopRecordingProfile: function()
   1331     {
   1332         this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
   1333         /**
   1334          * @param {?string} error
   1335          * @this {WebInspector.HeapSnapshotProfileType}
   1336          */
   1337         function didTakeHeapSnapshot(error)
   1338         {
   1339             var profile = this.profileBeingRecorded();
   1340             if (!profile)
   1341                 return;
   1342             profile._finishLoad();
   1343             this._profileSamples = null;
   1344             this.setProfileBeingRecorded(null);
   1345             this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
   1346         }
   1347 
   1348         this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
   1349         this._recording = false;
   1350         this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
   1351     },
   1352 
   1353     _toggleRecording: function()
   1354     {
   1355         if (this._recording)
   1356             this._stopRecordingProfile();
   1357         else
   1358             this._startRecordingProfile();
   1359         return this._recording;
   1360     },
   1361 
   1362     get treeItemTitle()
   1363     {
   1364         return WebInspector.UIString("HEAP TIMELINES");
   1365     },
   1366 
   1367     get description()
   1368     {
   1369         return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
   1370     },
   1371 
   1372     /**
   1373      * @override
   1374      */
   1375     resetProfiles: function()
   1376     {
   1377         var wasRecording = this._recording;
   1378         var recordingAllocationStacks = wasRecording && this.profileBeingRecorded()._hasAllocationStacks;
   1379         // Clear current profile to avoid stopping backend.
   1380         this.setProfileBeingRecorded(null);
   1381         WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
   1382         this._profileSamples = null;
   1383         this._lastSeenIndex = -1;
   1384         if (wasRecording)
   1385             this._addNewProfile(recordingAllocationStacks);
   1386     },
   1387 
   1388     /**
   1389      * @override
   1390      */
   1391     profileBeingRecordedRemoved: function()
   1392     {
   1393         this._stopRecordingProfile();
   1394         this._profileSamples = null;
   1395     },
   1396 
   1397     __proto__: WebInspector.HeapSnapshotProfileType.prototype
   1398 }
   1399 
   1400 /**
   1401  * @constructor
   1402  * @extends {WebInspector.ProfileHeader}
   1403  * @param {?WebInspector.Target} target
   1404  * @param {!WebInspector.HeapSnapshotProfileType} type
   1405  * @param {string=} title
   1406  * @param {boolean=} hasAllocationStacks
   1407  */
   1408 WebInspector.HeapProfileHeader = function(target, type, title, hasAllocationStacks)
   1409 {
   1410     WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid()));
   1411     this._hasAllocationStacks = !!hasAllocationStacks;
   1412     this.maxJSObjectId = -1;
   1413     /**
   1414      * @type {?WebInspector.HeapSnapshotWorkerProxy}
   1415      */
   1416     this._workerProxy = null;
   1417     /**
   1418      * @type {?WebInspector.OutputStream}
   1419      */
   1420     this._receiver = null;
   1421     /**
   1422      * @type {?WebInspector.HeapSnapshotProxy}
   1423      */
   1424     this._snapshotProxy = null;
   1425     /**
   1426      * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
   1427      */
   1428     this._loadCallbacks = [];
   1429     this._totalNumberOfChunks = 0;
   1430     this._bufferedWriter = null;
   1431 }
   1432 
   1433 WebInspector.HeapProfileHeader.prototype = {
   1434     /**
   1435      * @override
   1436      * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
   1437      * @return {!WebInspector.ProfileSidebarTreeElement}
   1438      */
   1439     createSidebarTreeElement: function(dataDisplayDelegate)
   1440     {
   1441         return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
   1442     },
   1443 
   1444     /**
   1445      * @override
   1446      * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
   1447      * @return {!WebInspector.HeapSnapshotView}
   1448      */
   1449     createView: function(dataDisplayDelegate)
   1450     {
   1451         return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
   1452     },
   1453 
   1454     /**
   1455      * @override
   1456      * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
   1457      */
   1458     load: function(callback)
   1459     {
   1460         if (this.uid === -1)
   1461             return;
   1462         if (this._snapshotProxy) {
   1463             callback(this._snapshotProxy);
   1464             return;
   1465         }
   1466         this._loadCallbacks.push(callback);
   1467     },
   1468 
   1469     _prepareToLoad: function()
   1470     {
   1471         console.assert(!this._receiver, "Already loading");
   1472         this._setupWorker();
   1473         this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
   1474     },
   1475 
   1476     _finishLoad: function()
   1477     {
   1478         if (!this._wasDisposed)
   1479             this._receiver.close();
   1480         if (this._bufferedWriter) {
   1481             this._bufferedWriter.finishWriting(this._didWriteToTempFile.bind(this));
   1482             this._bufferedWriter = null;
   1483         }
   1484     },
   1485 
   1486     _didWriteToTempFile: function(tempFile)
   1487     {
   1488         if (this._wasDisposed) {
   1489             if (tempFile)
   1490                 tempFile.remove();
   1491             return;
   1492         }
   1493         this._tempFile = tempFile;
   1494         if (!tempFile)
   1495             this._failedToCreateTempFile = true;
   1496         if (this._onTempFileReady) {
   1497             this._onTempFileReady();
   1498             this._onTempFileReady = null;
   1499         }
   1500     },
   1501 
   1502     _setupWorker: function()
   1503     {
   1504         /**
   1505          * @this {WebInspector.HeapProfileHeader}
   1506          */
   1507         function setProfileWait(event)
   1508         {
   1509             this.updateStatus(null, event.data);
   1510         }
   1511         console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
   1512         this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
   1513         this._workerProxy.addEventListener("wait", setProfileWait, this);
   1514         this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
   1515     },
   1516 
   1517     /**
   1518      * @param {string} eventName
   1519      * @param {*} data
   1520      */
   1521     _handleWorkerEvent: function(eventName, data)
   1522     {
   1523         if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
   1524             return;
   1525         var subtitle = /** @type {string} */ (data);
   1526         this.updateStatus(subtitle);
   1527     },
   1528 
   1529     /**
   1530      * @override
   1531      */
   1532     dispose: function()
   1533     {
   1534         if (this._workerProxy)
   1535             this._workerProxy.dispose();
   1536         this.removeTempFile();
   1537         this._wasDisposed = true;
   1538     },
   1539 
   1540     _didCompleteSnapshotTransfer: function()
   1541     {
   1542         if (!this._snapshotProxy)
   1543             return;
   1544         this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
   1545     },
   1546 
   1547     /**
   1548      * @param {string} chunk
   1549      */
   1550     transferChunk: function(chunk)
   1551     {
   1552         if (!this._bufferedWriter)
   1553             this._bufferedWriter = new WebInspector.DeferredTempFile("heap-profiler", String(this.uid));
   1554         this._bufferedWriter.write([chunk]);
   1555 
   1556         ++this._totalNumberOfChunks;
   1557         this._receiver.write(chunk, function() {});
   1558     },
   1559 
   1560     _snapshotReceived: function(snapshotProxy)
   1561     {
   1562         if (this._wasDisposed)
   1563             return;
   1564         this._receiver = null;
   1565         this._snapshotProxy = snapshotProxy;
   1566         this.maxJSObjectId = snapshotProxy.maxJSObjectId();
   1567         this._didCompleteSnapshotTransfer();
   1568         this._workerProxy.startCheckingForLongRunningCalls();
   1569         this.notifySnapshotReceived();
   1570     },
   1571 
   1572     notifySnapshotReceived: function()
   1573     {
   1574         for (var i = 0; i < this._loadCallbacks.length; i++)
   1575             this._loadCallbacks[i](/** @type {!WebInspector.HeapSnapshotProxy} */ (this._snapshotProxy));
   1576         this._loadCallbacks = null;
   1577         this._profileType._snapshotReceived(this);
   1578         if (this.canSaveToFile())
   1579             this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
   1580     },
   1581 
   1582     // Hook point for tests.
   1583     _wasShown: function()
   1584     {
   1585     },
   1586 
   1587     /**
   1588      * @override
   1589      * @return {boolean}
   1590      */
   1591     canSaveToFile: function()
   1592     {
   1593         return !this.fromFile() && !!this._snapshotProxy;
   1594     },
   1595 
   1596     /**
   1597      * @override
   1598      */
   1599     saveToFile: function()
   1600     {
   1601         var fileOutputStream = new WebInspector.FileOutputStream();
   1602 
   1603         /**
   1604          * @param {boolean} accepted
   1605          * @this {WebInspector.HeapProfileHeader}
   1606          */
   1607         function onOpen(accepted)
   1608         {
   1609             if (!accepted)
   1610                 return;
   1611             if (this._failedToCreateTempFile) {
   1612                 WebInspector.console.error("Failed to open temp file with heap snapshot");
   1613                 fileOutputStream.close();
   1614             } else if (this._tempFile) {
   1615                 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
   1616                 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
   1617             } else {
   1618                 this._onTempFileReady = onOpen.bind(this, accepted);
   1619                 this._updateSaveProgress(0, 1);
   1620             }
   1621         }
   1622         this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
   1623         fileOutputStream.open(this._fileName, onOpen.bind(this));
   1624     },
   1625 
   1626     _updateSaveProgress: function(value, total)
   1627     {
   1628         var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
   1629         this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
   1630     },
   1631 
   1632     /**
   1633      * @override
   1634      * @param {!File} file
   1635      */
   1636     loadFromFile: function(file)
   1637     {
   1638         this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
   1639         this._setupWorker();
   1640         var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
   1641         var fileReader = this._createFileReader(file, delegate);
   1642         fileReader.start(this._receiver);
   1643     },
   1644 
   1645     _createFileReader: function(file, delegate)
   1646     {
   1647         return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
   1648     },
   1649 
   1650     __proto__: WebInspector.ProfileHeader.prototype
   1651 }
   1652 
   1653 /**
   1654  * @constructor
   1655  * @implements {WebInspector.OutputStreamDelegate}
   1656  */
   1657 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
   1658 {
   1659     this._snapshotHeader = snapshotHeader;
   1660 }
   1661 
   1662 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
   1663     onTransferStarted: function()
   1664     {
   1665     },
   1666 
   1667     /**
   1668      * @param {!WebInspector.ChunkedReader} reader
   1669      */
   1670     onChunkTransferred: function(reader)
   1671     {
   1672     },
   1673 
   1674     onTransferFinished: function()
   1675     {
   1676     },
   1677 
   1678     /**
   1679      * @param {!WebInspector.ChunkedReader} reader
   1680      * @param {!Event} e
   1681      */
   1682     onError: function (reader, e)
   1683     {
   1684         var subtitle;
   1685         switch(e.target.error.code) {
   1686         case e.target.error.NOT_FOUND_ERR:
   1687             subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
   1688             break;
   1689         case e.target.error.NOT_READABLE_ERR:
   1690             subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
   1691             break;
   1692         case e.target.error.ABORT_ERR:
   1693             return;
   1694         default:
   1695             subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
   1696         }
   1697         this._snapshotHeader.updateStatus(subtitle);
   1698     }
   1699 }
   1700 
   1701 /**
   1702  * @constructor
   1703  * @implements {WebInspector.OutputStreamDelegate}
   1704  * @param {!WebInspector.HeapProfileHeader} profileHeader
   1705  */
   1706 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
   1707 {
   1708     this._profileHeader = profileHeader;
   1709 }
   1710 
   1711 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
   1712     onTransferStarted: function()
   1713     {
   1714         this._profileHeader._updateSaveProgress(0, 1);
   1715     },
   1716 
   1717     onTransferFinished: function()
   1718     {
   1719         this._profileHeader._didCompleteSnapshotTransfer();
   1720     },
   1721 
   1722     /**
   1723      * @param {!WebInspector.ChunkedReader} reader
   1724      */
   1725     onChunkTransferred: function(reader)
   1726     {
   1727         this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
   1728     },
   1729 
   1730     /**
   1731      * @param {!WebInspector.ChunkedReader} reader
   1732      * @param {!Event} event
   1733      */
   1734     onError: function(reader, event)
   1735     {
   1736         WebInspector.console.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message);
   1737         this.onTransferFinished();
   1738     }
   1739 }
   1740 
   1741 /**
   1742  * @constructor
   1743  * @extends {WebInspector.VBox}
   1744  * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
   1745  */
   1746 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
   1747 {
   1748     WebInspector.VBox.call(this);
   1749     this.registerRequiredCSS("flameChart.css");
   1750     this.element.id = "heap-recording-view";
   1751     this.element.classList.add("heap-tracking-overview");
   1752 
   1753     this._overviewContainer = this.element.createChild("div", "overview-container");
   1754     this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
   1755     this._overviewGrid.element.classList.add("fill");
   1756 
   1757     this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
   1758     this._overviewContainer.appendChild(this._overviewGrid.element);
   1759     this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
   1760     this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
   1761 
   1762     this._profileSamples = heapProfileHeader._profileSamples;
   1763     if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
   1764         this._profileType = heapProfileHeader.profileType();
   1765         this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
   1766         this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
   1767     }
   1768     var timestamps = this._profileSamples.timestamps;
   1769     var totalTime = this._profileSamples.totalTime;
   1770     this._windowLeft = 0.0;
   1771     this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
   1772     this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
   1773     this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
   1774     this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
   1775 }
   1776 
   1777 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
   1778 
   1779 WebInspector.HeapTrackingOverviewGrid.prototype = {
   1780     _onStopTracking: function(event)
   1781     {
   1782         this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
   1783         this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
   1784     },
   1785 
   1786     _onHeapStatsUpdate: function(event)
   1787     {
   1788         this._profileSamples = event.data;
   1789         this._scheduleUpdate();
   1790     },
   1791 
   1792      /**
   1793       * @param {number} width
   1794       * @param {number} height
   1795       */
   1796     _drawOverviewCanvas: function(width, height)
   1797     {
   1798         if (!this._profileSamples)
   1799             return;
   1800         var profileSamples = this._profileSamples;
   1801         var sizes = profileSamples.sizes;
   1802         var topSizes = profileSamples.max;
   1803         var timestamps = profileSamples.timestamps;
   1804         var startTime = timestamps[0];
   1805         var endTime = timestamps[timestamps.length - 1];
   1806 
   1807         var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
   1808         var maxSize = 0;
   1809         /**
   1810           * @param {!Array.<number>} sizes
   1811           * @param {function(number, number):void} callback
   1812           */
   1813         function aggregateAndCall(sizes, callback)
   1814         {
   1815             var size = 0;
   1816             var currentX = 0;
   1817             for (var i = 1; i < timestamps.length; ++i) {
   1818                 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
   1819                 if (x !== currentX) {
   1820                     if (size)
   1821                         callback(currentX, size);
   1822                     size = 0;
   1823                     currentX = x;
   1824                 }
   1825                 size += sizes[i];
   1826             }
   1827             callback(currentX, size);
   1828         }
   1829 
   1830         /**
   1831           * @param {number} x
   1832           * @param {number} size
   1833           */
   1834         function maxSizeCallback(x, size)
   1835         {
   1836             maxSize = Math.max(maxSize, size);
   1837         }
   1838 
   1839         aggregateAndCall(sizes, maxSizeCallback);
   1840 
   1841         var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
   1842 
   1843         this._overviewCanvas.width = width * window.devicePixelRatio;
   1844         this._overviewCanvas.height = height * window.devicePixelRatio;
   1845         this._overviewCanvas.style.width = width + "px";
   1846         this._overviewCanvas.style.height = height + "px";
   1847 
   1848         var context = this._overviewCanvas.getContext("2d");
   1849         context.scale(window.devicePixelRatio, window.devicePixelRatio);
   1850 
   1851         context.beginPath();
   1852         context.lineWidth = 2;
   1853         context.strokeStyle = "rgba(192, 192, 192, 0.6)";
   1854         var currentX = (endTime - startTime) * scaleFactor;
   1855         context.moveTo(currentX, height - 1);
   1856         context.lineTo(currentX, 0);
   1857         context.stroke();
   1858         context.closePath();
   1859 
   1860         var gridY;
   1861         var gridValue;
   1862         var gridLabelHeight = 14;
   1863         if (yScaleFactor) {
   1864             const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
   1865             // The round value calculation is a bit tricky, because
   1866             // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
   1867             // e.g. a round value 10KB is 10240 bytes.
   1868             gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
   1869             gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
   1870             if (gridValue * 5 <= maxGridValue)
   1871                 gridValue *= 5;
   1872             gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
   1873             context.beginPath();
   1874             context.lineWidth = 1;
   1875             context.strokeStyle = "rgba(0, 0, 0, 0.2)";
   1876             context.moveTo(0, gridY);
   1877             context.lineTo(width, gridY);
   1878             context.stroke();
   1879             context.closePath();
   1880         }
   1881 
   1882         /**
   1883           * @param {number} x
   1884           * @param {number} size
   1885           */
   1886         function drawBarCallback(x, size)
   1887         {
   1888             context.moveTo(x, height - 1);
   1889             context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
   1890         }
   1891 
   1892         context.beginPath();
   1893         context.lineWidth = 2;
   1894         context.strokeStyle = "rgba(192, 192, 192, 0.6)";
   1895         aggregateAndCall(topSizes, drawBarCallback);
   1896         context.stroke();
   1897         context.closePath();
   1898 
   1899         context.beginPath();
   1900         context.lineWidth = 2;
   1901         context.strokeStyle = "rgba(0, 0, 192, 0.8)";
   1902         aggregateAndCall(sizes, drawBarCallback);
   1903         context.stroke();
   1904         context.closePath();
   1905 
   1906         if (gridValue) {
   1907             var label = Number.bytesToString(gridValue);
   1908             var labelPadding = 4;
   1909             var labelX = 0;
   1910             var labelY = gridY - 0.5;
   1911             var labelWidth = 2 * labelPadding + context.measureText(label).width;
   1912             context.beginPath();
   1913             context.textBaseline = "bottom";
   1914             context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
   1915             context.fillStyle = "rgba(255, 255, 255, 0.75)";
   1916             context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
   1917             context.fillStyle = "rgb(64, 64, 64)";
   1918             context.fillText(label, labelX + labelPadding, labelY);
   1919             context.fill();
   1920             context.closePath();
   1921         }
   1922     },
   1923 
   1924     onResize: function()
   1925     {
   1926         this._updateOverviewCanvas = true;
   1927         this._scheduleUpdate();
   1928     },
   1929 
   1930     _onWindowChanged: function()
   1931     {
   1932         if (!this._updateGridTimerId)
   1933             this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
   1934     },
   1935 
   1936     _scheduleUpdate: function()
   1937     {
   1938         if (this._updateTimerId)
   1939             return;
   1940         this._updateTimerId = setTimeout(this.update.bind(this), 10);
   1941     },
   1942 
   1943     _updateBoundaries: function()
   1944     {
   1945         this._windowLeft = this._overviewGrid.windowLeft();
   1946         this._windowRight = this._overviewGrid.windowRight();
   1947         this._windowWidth = this._windowRight - this._windowLeft;
   1948     },
   1949 
   1950     update: function()
   1951     {
   1952         this._updateTimerId = null;
   1953         if (!this.isShowing())
   1954             return;
   1955         this._updateBoundaries();
   1956         this._overviewCalculator._updateBoundaries(this);
   1957         this._overviewGrid.updateDividers(this._overviewCalculator);
   1958         this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
   1959     },
   1960 
   1961     _updateGrid: function()
   1962     {
   1963         this._updateGridTimerId = 0;
   1964         this._updateBoundaries();
   1965         var ids = this._profileSamples.ids;
   1966         var timestamps = this._profileSamples.timestamps;
   1967         var sizes = this._profileSamples.sizes;
   1968         var startTime = timestamps[0];
   1969         var totalTime = this._profileSamples.totalTime;
   1970         var timeLeft = startTime + totalTime * this._windowLeft;
   1971         var timeRight = startTime + totalTime * this._windowRight;
   1972         var minId = 0;
   1973         var maxId = ids[ids.length - 1] + 1;
   1974         var size = 0;
   1975         for (var i = 0; i < timestamps.length; ++i) {
   1976             if (!timestamps[i])
   1977                 continue;
   1978             if (timestamps[i] > timeRight)
   1979                 break;
   1980             maxId = ids[i];
   1981             if (timestamps[i] < timeLeft) {
   1982                 minId = ids[i];
   1983                 continue;
   1984             }
   1985             size += sizes[i];
   1986         }
   1987 
   1988         this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
   1989     },
   1990 
   1991     __proto__: WebInspector.VBox.prototype
   1992 }
   1993 
   1994 
   1995 /**
   1996  * @constructor
   1997  */
   1998 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
   1999 {
   2000     this._lastUpdate = 0;
   2001     this._currentScale = 0.0;
   2002 }
   2003 
   2004 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
   2005     /**
   2006      * @param {number} target
   2007      * @return {number}
   2008      */
   2009     nextScale: function(target) {
   2010         target = target || this._currentScale;
   2011         if (this._currentScale) {
   2012             var now = Date.now();
   2013             var timeDeltaMs = now - this._lastUpdate;
   2014             this._lastUpdate = now;
   2015             var maxChangePerSec = 20;
   2016             var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
   2017             var scaleChange = target / this._currentScale;
   2018             this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
   2019         } else
   2020             this._currentScale = target;
   2021         return this._currentScale;
   2022     }
   2023 }
   2024 
   2025 
   2026 /**
   2027  * @constructor
   2028  * @implements {WebInspector.TimelineGrid.Calculator}
   2029  */
   2030 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
   2031 {
   2032 }
   2033 
   2034 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
   2035     /**
   2036      * @return {number}
   2037      */
   2038     paddingLeft: function()
   2039     {
   2040         return 0;
   2041     },
   2042 
   2043     /**
   2044      * @param {!WebInspector.HeapTrackingOverviewGrid} chart
   2045      */
   2046     _updateBoundaries: function(chart)
   2047     {
   2048         this._minimumBoundaries = 0;
   2049         this._maximumBoundaries = chart._profileSamples.totalTime;
   2050         this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
   2051     },
   2052 
   2053     /**
   2054      * @param {number} time
   2055      * @return {number}
   2056      */
   2057     computePosition: function(time)
   2058     {
   2059         return (time - this._minimumBoundaries) * this._xScaleFactor;
   2060     },
   2061 
   2062     /**
   2063      * @param {number} value
   2064      * @param {number=} precision
   2065      * @return {string}
   2066      */
   2067     formatTime: function(value, precision)
   2068     {
   2069         return Number.secondsToString(value / 1000, !!precision);
   2070     },
   2071 
   2072     /**
   2073      * @return {number}
   2074      */
   2075     maximumBoundary: function()
   2076     {
   2077         return this._maximumBoundaries;
   2078     },
   2079 
   2080     /**
   2081      * @return {number}
   2082      */
   2083     minimumBoundary: function()
   2084     {
   2085         return this._minimumBoundaries;
   2086     },
   2087 
   2088     /**
   2089      * @return {number}
   2090      */
   2091     zeroTime: function()
   2092     {
   2093         return this._minimumBoundaries;
   2094     },
   2095 
   2096     /**
   2097      * @return {number}
   2098      */
   2099     boundarySpan: function()
   2100     {
   2101         return this._maximumBoundaries - this._minimumBoundaries;
   2102     }
   2103 }
   2104 
   2105 
   2106 /**
   2107  * @constructor
   2108  * @extends {WebInspector.VBox}
   2109  */
   2110 WebInspector.HeapSnapshotStatisticsView = function()
   2111 {
   2112     WebInspector.VBox.call(this);
   2113     this.setMinimumSize(50, 25);
   2114     this._pieChart = new WebInspector.PieChart(150, WebInspector.HeapSnapshotStatisticsView._valueFormatter);
   2115     this.element.appendChild(this._pieChart.element);
   2116     this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
   2117 }
   2118 
   2119 /**
   2120  * @param {number} value
   2121  * @return {string}
   2122  */
   2123 WebInspector.HeapSnapshotStatisticsView._valueFormatter = function(value)
   2124 {
   2125     return WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
   2126 }
   2127 
   2128 WebInspector.HeapSnapshotStatisticsView.prototype = {
   2129     /**
   2130      * @param {number} value
   2131      */
   2132     setTotal: function(value)
   2133     {
   2134         this._pieChart.setTotal(value);
   2135     },
   2136 
   2137     /**
   2138      * @param {number} value
   2139      * @param {string} name
   2140      * @param {string=} color
   2141      */
   2142     addRecord: function(value, name, color)
   2143     {
   2144         if (color)
   2145             this._pieChart.addSlice(value, color);
   2146 
   2147         var node = this._labels.createChild("div");
   2148         var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
   2149         var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
   2150         var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
   2151         if (color)
   2152             swatchDiv.style.backgroundColor = color;
   2153         else
   2154             swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
   2155         nameDiv.textContent = name;
   2156         sizeDiv.textContent = WebInspector.HeapSnapshotStatisticsView._valueFormatter(value);
   2157     },
   2158 
   2159     __proto__: WebInspector.VBox.prototype
   2160 }
   2161 
   2162 /**
   2163  * @constructor
   2164  * @extends {WebInspector.View}
   2165  * @param {?WebInspector.Target} target
   2166  */
   2167 WebInspector.HeapAllocationStackView = function(target)
   2168 {
   2169     WebInspector.View.call(this);
   2170     this._target = target;;
   2171     this._linkifier = new WebInspector.Linkifier();
   2172 }
   2173 
   2174 WebInspector.HeapAllocationStackView.prototype = {
   2175     /**
   2176      * @param {!WebInspector.HeapSnapshotProxy} snapshot
   2177      * @param {number} snapshotNodeIndex
   2178      */
   2179     setAllocatedObject: function(snapshot, snapshotNodeIndex)
   2180     {
   2181         this.clear();
   2182         snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
   2183     },
   2184 
   2185     clear: function()
   2186     {
   2187         this.element.removeChildren();
   2188         this._linkifier.reset();
   2189     },
   2190 
   2191     /**
   2192      * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
   2193      */
   2194     _didReceiveAllocationStack: function(frames)
   2195     {
   2196         if (!frames) {
   2197             var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
   2198             stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
   2199             return;
   2200         }
   2201 
   2202         var stackDiv = this.element.createChild("div", "heap-allocation-stack");
   2203         for (var i = 0; i < frames.length; i++) {
   2204             var frame = frames[i];
   2205             var frameDiv = stackDiv.createChild("div", "stack-frame");
   2206             var name = frameDiv.createChild("div");
   2207             name.textContent = frame.functionName;
   2208             if (frame.scriptId) {
   2209                 var urlElement = this._linkifier.linkifyScriptLocation(this._target, String(frame.scriptId), frame.scriptName, frame.line - 1, frame.column - 1);
   2210                 frameDiv.appendChild(urlElement);
   2211             }
   2212         }
   2213     },
   2214 
   2215     __proto__: WebInspector.View.prototype
   2216 }
   2217