• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
43WebInspector.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 */
183WebInspector.HeapSnapshotView.Perspective = function(title)
184{
185    this._title = title;
186}
187
188WebInspector.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 */
243WebInspector.HeapSnapshotView.SummaryPerspective = function()
244{
245    WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Summary"));
246}
247
248WebInspector.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 */
293WebInspector.HeapSnapshotView.ComparisonPerspective = function()
294{
295    WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Comparison"));
296}
297
298WebInspector.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 */
338WebInspector.HeapSnapshotView.ContainmentPerspective = function()
339{
340    WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Containment"));
341}
342
343WebInspector.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 */
371WebInspector.HeapSnapshotView.DominatorPerspective = function()
372{
373    WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Dominators"));
374}
375
376WebInspector.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 */
405WebInspector.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
419WebInspector.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 */
465WebInspector.HeapSnapshotView.StatisticsPerspective = function()
466{
467    WebInspector.HeapSnapshotView.Perspective.call(this,  WebInspector.UIString("Statistics"));
468}
469
470WebInspector.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
494WebInspector.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 */
1055WebInspector.HeapProfilerDispatcher = function()
1056{
1057    this._dispatchers = [];
1058    InspectorBackend.registerHeapProfilerDispatcher(this);
1059}
1060
1061WebInspector.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
1125WebInspector.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 */
1134WebInspector.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
1140WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1141WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1142
1143WebInspector.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 */
1292WebInspector.TrackingHeapSnapshotProfileType = function()
1293{
1294    WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1295}
1296
1297WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1298
1299WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1300WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1301WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1302
1303WebInspector.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 */
1482WebInspector.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
1506WebInspector.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 */
1730WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1731{
1732    this._snapshotHeader = snapshotHeader;
1733}
1734
1735WebInspector.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 */
1779WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1780{
1781    this._profileHeader = profileHeader;
1782}
1783
1784WebInspector.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 */
1819WebInspector.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
1850WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1851
1852WebInspector.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 */
2071WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
2072{
2073    this._lastUpdate = 0;
2074    this._currentScale = 0.0;
2075}
2076
2077WebInspector.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 */
2103WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2104{
2105}
2106
2107WebInspector.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 */
2183WebInspector.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
2193WebInspector.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 */
2231WebInspector.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
2238WebInspector.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