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