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