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