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