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