tizen beta release
[framework/web/webkit-efl.git] / Source / WebCore / inspector / front-end / DetailedHeapshotView.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 WebInspector.HeapSnapshotSortableDataGrid = function(columns)
32 {
33     WebInspector.DataGrid.call(this, columns);
34     this.addEventListener("sorting changed", this.sortingChanged, this);
35 }
36
37 WebInspector.HeapSnapshotSortableDataGrid.prototype = {
38     dispose: function()
39     {
40         for (var i = 0, l = this.children.length; i < l; ++i)
41             this.children[i].dispose();
42     },
43
44     resetSortingCache: function()
45     {
46         delete this._lastSortColumnIdentifier;
47         delete this._lastSortAscending;
48     },
49
50     sortingChanged: function()
51     {
52         var sortAscending = this.sortOrder === "ascending";
53         var sortColumnIdentifier = this.sortColumnIdentifier;
54         if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
55             return;
56         this._lastSortColumnIdentifier = sortColumnIdentifier;
57         this._lastSortAscending = sortAscending;
58         var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
59
60         function SortByTwoFields(nodeA, nodeB)
61         {
62             var field1 = nodeA[sortFields[0]];
63             var field2 = nodeB[sortFields[0]];
64             var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
65             if (!sortFields[1])
66                 result = -result;
67             if (result !== 0)
68                 return result;
69             field1 = nodeA[sortFields[2]];
70             field2 = nodeB[sortFields[2]];
71             result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
72             if (!sortFields[3])
73                 result = -result;
74             return result;
75         }
76         this._performSorting(SortByTwoFields);
77     },
78
79     _performSorting: function(sortFunction)
80     {
81         this.recursiveSortingEnter();
82         var children = this.children;
83         this.removeChildren();
84         children.sort(sortFunction);
85         for (var i = 0, l = children.length; i < l; ++i) {
86             var child = children[i];
87             this.appendChild(child);
88             if (child.expanded)
89                 child.sort();
90         }
91         this.recursiveSortingLeave();
92     },
93
94     recursiveSortingEnter: function()
95     {
96         if (!("_recursiveSortingDepth" in this))
97             this._recursiveSortingDepth = 1;
98         else
99             ++this._recursiveSortingDepth;
100     },
101
102     recursiveSortingLeave: function()
103     {
104         if (!("_recursiveSortingDepth" in this))
105             return;
106         if (!--this._recursiveSortingDepth) {
107             delete this._recursiveSortingDepth;
108             this.dispatchEventToListeners("sorting complete");
109         }
110     }
111 };
112
113 WebInspector.HeapSnapshotSortableDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype;
114
115 WebInspector.HeapSnapshotContainmentDataGrid = function()
116 {
117     var columns = {
118         object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true, sort: "ascending" },
119         shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
120         retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sortable: true }
121     };
122     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
123 }
124
125 WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
126     _defaultPopulateCount: 100,
127
128     expandRoute: function(route)
129     {
130         function nextStep(parent, hopIndex)
131         {
132             if (hopIndex >= route.length) {
133                 parent.element.scrollIntoViewIfNeeded(true);
134                 parent.select();
135                 return;
136             }
137             var nodeIndex = route[hopIndex];
138             for (var i = 0, l = parent.children.length; i < l; ++i) {
139                 var child = parent.children[i];
140                 if (child.snapshotNodeIndex === nodeIndex) {
141                     if (child.expanded)
142                         nextStep(child, hopIndex + 1);
143                     else {
144                         function afterExpand()
145                         {
146                             child.removeEventListener("populate complete", afterExpand, null);
147                             var lastChild = child.children[child.children.length - 1];
148                             if (!lastChild.showAll)
149                                 nextStep(child, hopIndex + 1);
150                             else {
151                                 child.addEventListener("populate complete", afterExpand, null);
152                                 lastChild.showAll.click();
153                             }
154                         }
155                         child.addEventListener("populate complete", afterExpand, null);
156                         child.expand();
157                     }
158                     break;
159                 }
160             }
161         }
162         nextStep(this, 0);
163     },
164
165     setDataSource: function(snapshotView, snapshot)
166     {
167         this.snapshotView = snapshotView;
168         this.snapshot = snapshot;
169         this.snapshotNodeIndex = this.snapshot.rootNodeIndex;
170         this._provider = this._createProvider(snapshot, this.snapshotNodeIndex);
171         this.sort();
172     },
173
174     sortingChanged: function()
175     {
176         this.sort();
177     }
178 };
179
180 MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotObjectNode.prototype, WebInspector.HeapSnapshotContainmentDataGrid.prototype);
181 WebInspector.HeapSnapshotContainmentDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
182
183 WebInspector.HeapSnapshotConstructorsDataGrid = function()
184 {
185     var columns = {
186         object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true },
187         count: { title: WebInspector.UIString("#"), width: "45px", sortable: true },
188         shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
189         retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
190     };
191     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
192     this._filterProfileIndex = -1;
193 }
194
195 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
196     _defaultPopulateCount: 100,
197
198     _sortFields: function(sortColumn, sortAscending)
199     {
200         return {
201             object: ["_name", sortAscending, "_count", false],
202             count: ["_count", sortAscending, "_name", true],
203             shallowSize: ["_shallowSize", sortAscending, "_name", true],
204             retainedSize: ["_retainedSize", sortAscending, "_name", true]
205         }[sortColumn];
206     },
207
208     setDataSource: function(snapshotView, snapshot)
209     {
210         this.snapshotView = snapshotView;
211         this.snapshot = snapshot;
212         if (this._filterProfileIndex === -1)
213             this.populateChildren();
214     },
215
216     populateChildren: function()
217     {
218         function aggregatesReceived(key, aggregates)
219         {
220             for (var constructor in aggregates)
221                 this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key));
222             this.sortingChanged();
223         }
224
225         if (this._filterProfileIndex === -1) {
226             this.snapshot.aggregates(false, "allObjects", null, aggregatesReceived.bind(this, "allObjects"));
227             return;
228         }
229
230         this.dispose();
231         this.removeChildren();
232         this.resetSortingCache();
233
234         var key = this._minNodeId + ".." + this._maxNodeId;
235         var filter = "function(node) { var id = node.id; return id > " + this._minNodeId + " && id <= " + this._maxNodeId + "; }";
236         this.snapshot.aggregates(false, key, filter, aggregatesReceived.bind(this, key));
237     },
238
239     _filterSelectIndexChanged: function(loader, profileIndex)
240     {
241         this._filterProfileIndex = profileIndex;
242
243         delete this._maxNodeId;
244         delete this._minNodeId;
245
246         if (this._filterProfileIndex === -1) {
247             this.populateChildren();
248             return;
249         }
250
251         function firstSnapshotLoaded(snapshot)
252         {
253             this._maxNodeId = snapshot.maxNodeId;
254             if (profileIndex > 0)
255                 loader(profileIndex - 1, secondSnapshotLoaded.bind(this));
256             else {
257                 this._minNodeId = 0;
258                 this.populateChildren();
259             }
260         }
261
262         function secondSnapshotLoaded(snapshot)
263         {
264             this._minNodeId = snapshot.maxNodeId;
265             this.populateChildren();
266         }
267
268         loader(profileIndex, firstSnapshotLoaded.bind(this));
269     },
270 };
271
272 WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
273
274 WebInspector.HeapSnapshotDiffDataGrid = function()
275 {
276     var columns = {
277         object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true },
278         addedCount: { title: WebInspector.UIString("# New"), width: "72px", sortable: true, sort: "descending" },
279         removedCount: { title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true },
280         // \u0394 is a Greek delta letter.
281         countDelta: { title: "\u0394", width: "40px", sortable: true },
282         addedSize: { title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true },
283         removedSize: { title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true },
284         sizeDelta: { title: "\u0394", width: "72px", sortable: true }
285     };
286     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
287 }
288
289 WebInspector.HeapSnapshotDiffDataGrid.prototype = {
290     _defaultPopulateCount: 50,
291
292     _sortFields: function(sortColumn, sortAscending)
293     {
294         return {
295             object: ["_name", sortAscending, "_count", false],
296             addedCount: ["_addedCount", sortAscending, "_name", true],
297             removedCount: ["_removedCount", sortAscending, "_name", true],
298             countDelta: ["_countDelta", sortAscending, "_name", true],
299             addedSize: ["_addedSize", sortAscending, "_name", true],
300             removedSize: ["_removedSize", sortAscending, "_name", true],
301             sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
302         }[sortColumn];
303     },
304
305     setDataSource: function(snapshotView, snapshot)
306     {
307         this.snapshotView = snapshotView;
308         this.snapshot = snapshot;
309     },
310
311     _baseProfileIndexChanged: function(loader, profileIndex)
312     {
313         loader(profileIndex, this.setBaseDataSource.bind(this));
314     },
315
316     setBaseDataSource: function(baseSnapshot)
317     {
318         this.baseSnapshot = baseSnapshot;
319         this.dispose();
320         this.removeChildren();
321         this.resetSortingCache();
322         if (this.baseSnapshot === this.snapshot) {
323             this.dispatchEventToListeners("sorting complete");
324             return;
325         }
326         this.populateChildren();
327     },
328
329     populateChildren: function()
330     {
331         function baseAggregatesReceived(baseClasses)
332         {
333             function aggregatesReceived(classes)
334             {
335                 var nodeCount = 0;
336                 var nodes = [];
337                 for (var clss in baseClasses)
338                     nodes.push(new WebInspector.HeapSnapshotDiffNode(this, clss, baseClasses[clss], classes[clss]));
339                 for (clss in classes) {
340                     if (!(clss in baseClasses))
341                         nodes.push(new WebInspector.HeapSnapshotDiffNode(this, clss, null, classes[clss]));
342                 }
343                 nodeCount = nodes.length;
344                 function addNodeIfNonZeroDiff(boundNode, zeroDiff)
345                 {
346                     if (!zeroDiff)
347                         this.appendChild(boundNode);
348                     if (!--nodeCount)
349                         this.sortingChanged();
350                 }
351                 for (var i = 0, l = nodes.length; i < l; ++i) {
352                     var node = nodes[i];
353                     node.calculateDiff(this, addNodeIfNonZeroDiff.bind(this, node));
354                 }
355             }
356             this.snapshot.aggregates(true, "allObjects", null, aggregatesReceived.bind(this));
357         }
358         this.baseSnapshot.aggregates(true, "allObjects", null, baseAggregatesReceived.bind(this));
359     }
360 };
361
362 WebInspector.HeapSnapshotDiffDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
363
364 WebInspector.HeapSnapshotDominatorsDataGrid = function()
365 {
366     var columns = {
367         object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true },
368         shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
369         retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
370     };
371     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
372 }
373
374 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
375     _defaultPopulateCount: 25,
376
377     setDataSource: function(snapshotView, snapshot)
378     {
379         this.snapshotView = snapshotView;
380         this.snapshot = snapshot;
381         this.snapshotNodeIndex = this.snapshot.rootNodeIndex;
382         this._provider = this._createProvider(snapshot, this.snapshotNodeIndex);
383         this.sort();
384     },
385
386     sortingChanged: function()
387     {
388         this.sort();
389     }
390 };
391
392 MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotDominatorObjectNode.prototype, WebInspector.HeapSnapshotDominatorsDataGrid.prototype);
393 WebInspector.HeapSnapshotDominatorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
394
395 WebInspector.HeapSnapshotPathFinderState = function(snapshot, nodeIndex, rootFilter)
396 {
397     this._pathFinder = snapshot.createPathFinder(nodeIndex, !WebInspector.DetailedHeapshotView.prototype.showHiddenData);
398     this._pathFinder.updateRoots(rootFilter);
399     this._foundCount = 0;
400     this._foundCountMax = null;
401     this._totalFoundCount = 0;
402     this._cancelled = false;
403 }
404
405 WebInspector.HeapSnapshotPathFinderState.prototype = {
406     batchDone: function(status)
407     {
408     },
409
410     pathFound: function(path)
411     {
412     },
413
414     cancel: function()
415     {
416         this._cancelled = true;
417         this._pathFinder.dispose();
418     },
419
420     startBatch: function(count)
421     {
422         if (this._cancelled)
423             return;
424         this._foundCount = 0;
425         this._foundCountMax = count;
426         this._pathFinder.findNext(this._pathFound.bind(this));
427     },
428
429     _pathFound: function(result)
430     {
431         if (this._cancelled)
432             return;
433         if (result === null) {
434             if (!this._totalFoundCount)
435                 this.batchDone("no-paths-at-all");
436         } else if (result !== false) {
437             this.pathFound(result);
438             ++this._foundCount;
439             ++this._totalFoundCount;
440             if (this._foundCount < this._foundCountMax)
441                 this._pathFinder.findNext(this._pathFound.bind(this));
442             else
443                 this.batchDone("have-more-paths");
444         } else {
445             this.batchDone("no-more-paths");
446         }
447     }
448 };
449
450 WebInspector.HeapSnapshotRetainingPathsList = function()
451 {
452     var columns = {
453         path: { title: WebInspector.UIString("Retaining path"), sortable: true },
454         len: { title: WebInspector.UIString("Length"), width: "90px", sortable: true, sort: "ascending" }
455     };
456     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
457     this._defaultPopulateCount = 100;
458     this._nodeIndex = null;
459     this._state = null;
460     this._prefix = null;
461 }
462
463 WebInspector.HeapSnapshotRetainingPathsList.prototype = {
464     dispose: function()
465     {
466         if (this._state)
467             this._state.cancel();
468     },
469
470     _sortFields: function(sortColumn, sortAscending)
471     {
472         return {
473             path: ["path", sortAscending, "len", true],
474             len: ["len", sortAscending, "path", true]
475         }[sortColumn];
476     },
477
478     _resetPaths: function()
479     {
480         var rootFilter = this.snapshotView.isTracingToWindowObjects ?
481             "function (node) { return node.name.substr(0, 9) === \"DOMWindow\"; }" : null;
482         if (this._state)
483             this._state.cancel();
484         this._state = new WebInspector.HeapSnapshotPathFinderState(this._snapshot, this._nodeIndex, rootFilter);
485         this._state.batchDone = this._batchDone.bind(this);
486         this._state.pathFound = this._pathFound.bind(this);
487         this.removeChildren();
488         this.resetSortingCache();
489         this.showNext(this._defaultPopulateCount);
490     },
491
492     setDataSource: function(snapshotView, snapshot, nodeIndex, prefix)
493     {
494         if (this._nodeIndex === nodeIndex)
495             return;
496         this.snapshotView = snapshotView;
497         this._snapshot = snapshot;
498         this._nodeIndex = nodeIndex;
499         this._prefix = prefix;
500         this._resetPaths();
501     },
502
503     refresh: function()
504     {
505         if (this.snapshotView)
506             this._resetPaths();
507     },
508
509     reset: function()
510     {
511         if (this._state)
512             this._state.cancel();
513         this.removeChildren();
514         this.resetSortingCache();
515         this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("Click on an object to show retaining paths"), len:""}, false));
516     },
517
518     _batchDone: function(state)
519     {
520         switch (state) {
521         case "no-paths-at-all":
522             this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("Can't find any paths."), len:""}, false));
523             break;
524         case "have-more-paths":
525             this.appendChild(new WebInspector.ShowMoreDataGridNode(this.showNext.bind(this), this._defaultPopulateCount));
526             this.resetSortingCache();
527             this.sortingChanged();
528             break;
529         case "no-more-paths":
530             // Nothing to do.
531             break;
532         }
533     },
534
535     _pathFound: function(result)
536     {
537         if (WebInspector.HeapSnapshotGenericObjectNode.prototype.isDOMWindow(result.path))
538             result.path = WebInspector.HeapSnapshotGenericObjectNode.prototype.shortenWindowURL(result.path, true);
539         if (this._prefix)
540             result.path = this._prefix + result.path;
541         var node = new WebInspector.DataGridNode(result, false);
542         node.route = result.route;
543         this.appendChild(node);
544     },
545
546     showNext: function(pathsCount)
547     {
548         this._state.startBatch(pathsCount);
549     },
550
551     _performSorting: function(sortFunction)
552     {
553         function DataExtractorWrapper(nodeA, nodeB)
554         {
555             return sortFunction(nodeA.data, nodeB.data);
556         }
557         this.recursiveSortingEnter();
558         this.sortNodes(DataExtractorWrapper);
559         this.recursiveSortingLeave();
560     }
561 };
562
563 WebInspector.HeapSnapshotRetainingPathsList.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
564
565 WebInspector.DetailedHeapshotView = function(parent, profile)
566 {
567     WebInspector.View.call(this);
568
569     this.element.addStyleClass("detailed-heapshot-view");
570
571     this.parent = parent;
572     this.parent.addEventListener("profile added", this._updateBaseOptions, this);
573     this.parent.addEventListener("profile added", this._updateFilterOptions, this);
574
575     this.showCountAsPercent = false;
576     this.showShallowSizeAsPercent = false;
577     this.showRetainedSizeAsPercent = false;
578
579     this.viewsContainer = document.createElement("div");
580     this.viewsContainer.addStyleClass("views-container");
581     this.element.appendChild(this.viewsContainer);
582
583     this.containmentView = new WebInspector.View();
584     this.containmentView.element.addStyleClass("view");
585     this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
586     this.containmentDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
587     this.containmentDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
588     this.containmentDataGrid.show(this.containmentView.element);
589
590     this.constructorsView = new WebInspector.View();
591     this.constructorsView.element.addStyleClass("view");
592     this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
593     this.constructorsDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
594     this.constructorsDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
595     this.constructorsDataGrid.show(this.constructorsView.element);
596
597     this.diffView = new WebInspector.View();
598     this.diffView.element.addStyleClass("view");
599     this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
600     this.diffDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
601     this.diffDataGrid.show(this.diffView.element);
602
603     this.dominatorView = new WebInspector.View();
604     this.dominatorView.element.addStyleClass("view");
605     this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
606     this.dominatorDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
607     this.dominatorDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
608     this.dominatorDataGrid.show(this.dominatorView.element);
609
610     this.retainmentViewHeader = document.createElement("div");
611     this.retainmentViewHeader.addStyleClass("retainers-view-header");
612     this.retainmentViewHeader.addEventListener("mousedown", this._startRetainersHeaderDragging.bind(this), true);
613     var retainingPathsTitleDiv = document.createElement("div");
614     retainingPathsTitleDiv.className = "title";
615     var retainingPathsTitle = document.createElement("span");
616     retainingPathsTitle.textContent = WebInspector.UIString("Paths from the selected object");
617     this.retainingPathsRoot = document.createElement("select");
618     this.retainingPathsRoot.className = "status-bar-item";
619     this.retainingPathsRoot.addEventListener("change", this._changeRetainingPathsRoot.bind(this), false);
620     var toGCRootsTraceOption = document.createElement("option");
621     toGCRootsTraceOption.label = WebInspector.UIString("to GC roots");
622     var toWindowObjectsTraceOption = document.createElement("option");
623     toWindowObjectsTraceOption.label = WebInspector.UIString("to window objects");
624     this.retainingPathsRoot.appendChild(toGCRootsTraceOption);
625     this.retainingPathsRoot.appendChild(toWindowObjectsTraceOption);
626     retainingPathsTitleDiv.appendChild(retainingPathsTitle);
627     retainingPathsTitleDiv.appendChild(this.retainingPathsRoot);
628     this.retainmentViewHeader.appendChild(retainingPathsTitleDiv);
629     this.element.appendChild(this.retainmentViewHeader);
630
631     this.retainmentView = new WebInspector.View();
632     this.retainmentView.element.addStyleClass("view");
633     this.retainmentView.element.addStyleClass("retaining-paths-view");
634     this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainingPathsList();
635     this.retainmentDataGrid.element.addEventListener("click", this._mouseClickInRetainmentGrid.bind(this), true);
636     this.retainmentDataGrid.show(this.retainmentView.element);
637     this.retainmentView.show(this.element);
638     this.retainmentDataGrid.reset();
639
640     this.dataGrid = this.constructorsDataGrid;
641     this.currentView = this.constructorsView;
642
643     this.viewSelectElement = document.createElement("select");
644     this.viewSelectElement.className = "status-bar-item";
645     this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false);
646
647     this.views = [{title: "Summary", view: this.constructorsView, grid: this.constructorsDataGrid},
648                   {title: "Comparison", view: this.diffView, grid: this.diffDataGrid},
649                   {title: "Containment", view: this.containmentView, grid: this.containmentDataGrid},
650                   {title: "Dominators", view: this.dominatorView, grid: this.dominatorDataGrid}];
651     this.views.current = 0;
652     for (var i = 0; i < this.views.length; ++i) {
653         var view = this.views[i];
654         var option = document.createElement("option");
655         option.label = WebInspector.UIString(view.title);
656         this.viewSelectElement.appendChild(option);
657     }
658
659     this._profileUid = profile.uid;
660
661     this.baseSelectElement = document.createElement("select");
662     this.baseSelectElement.className = "status-bar-item hidden";
663     this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false);
664     this._updateBaseOptions();
665
666     this.filterSelectElement = document.createElement("select");
667     this.filterSelectElement.className = "status-bar-item";
668     this.filterSelectElement.addEventListener("change", this._changeFilter.bind(this), false);
669     this._updateFilterOptions();
670
671     this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item");
672     this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
673     this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item");
674     this.helpButton.addEventListener("click", this._helpClicked.bind(this), false);
675
676     var popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._showObjectPopover.bind(this), null, true);
677
678     this._loadProfile(this._profileUid, profileCallback.bind(this));
679
680     function profileCallback()
681     {
682         var list = this._profiles();
683         var profileIndex;
684         for (var i = 0; i < list.length; ++i) {
685             if (list[i].uid === this._profileUid) {
686                 profileIndex = i;
687                 break;
688             }
689         }
690
691         if (profileIndex > 0)
692             this.baseSelectElement.selectedIndex = profileIndex - 1;
693         else
694             this.baseSelectElement.selectedIndex = profileIndex;
695         this.dataGrid.setDataSource(this, this.profileWrapper);
696         this._updatePercentButton();
697     }
698 }
699
700 WebInspector.DetailedHeapshotView.prototype = {
701     dispose: function()
702     {
703         this.profileWrapper.dispose();
704         if (this.baseProfile)
705             this.baseProfileWrapper.dispose();
706         this.containmentDataGrid.dispose();
707         this.constructorsDataGrid.dispose();
708         this.diffDataGrid.dispose();
709         this.dominatorDataGrid.dispose();
710         this.retainmentDataGrid.dispose();
711     },
712
713     get statusBarItems()
714     {
715         return [this.viewSelectElement, this.baseSelectElement, this.filterSelectElement, this.percentButton.element, this.helpButton.element];
716     },
717
718     get profile()
719     {
720         return this.parent.getProfile(WebInspector.DetailedHeapshotProfileType.TypeId, this._profileUid);
721     },
722
723     get profileWrapper()
724     {
725         return this.profile.proxy;
726     },
727
728     get baseProfile()
729     {
730         return this.parent.getProfile(WebInspector.DetailedHeapshotProfileType.TypeId, this._baseProfileUid);
731     },
732
733     get baseProfileWrapper()
734     {
735         return this.baseProfile.proxy;
736     },
737
738     wasShown: function()
739     {
740         if (!this.profileWrapper.loaded)
741             this._loadProfile(this._profileUid, profileCallback1.bind(this));
742         else
743             profileCallback1.call(this);
744
745         function profileCallback1() {
746             if (this.baseProfile && !this.baseProfileWrapper.loaded)
747                 this._loadProfile(this._baseProfileUid, profileCallback2.bind(this));
748             else
749                 profileCallback2.call(this);
750         }
751
752         function profileCallback2() {
753             this.currentView.show(this.viewsContainer);
754         }
755     },
756
757     willHide: function()
758     {
759         this._currentSearchResultIndex = -1;
760     },
761
762     onResize: function()
763     {
764         var height = this.retainmentView.element.clientHeight;
765         this._updateRetainmentViewHeight(height);
766     },
767
768     refreshShowAsPercents: function()
769     {
770         this._updatePercentButton();
771         this.refreshVisibleData();
772     },
773
774     searchCanceled: function()
775     {
776         if (this._searchResults) {
777             for (var i = 0; i < this._searchResults.length; ++i) {
778                 var node = this._searchResults[i].node;
779                 delete node._searchMatched;
780                 node.refresh();
781             }
782         }
783
784         delete this._searchFinishedCallback;
785         this._currentSearchResultIndex = -1;
786         this._searchResults = [];
787     },
788
789     performSearch: function(query, finishedCallback)
790     {
791         // Call searchCanceled since it will reset everything we need before doing a new search.
792         this.searchCanceled();
793
794         query = query.trim();
795
796         if (!query.length)
797             return;
798         if (this.currentView !== this.constructorsView && this.currentView !== this.diffView)
799             return;
800
801         this._searchFinishedCallback = finishedCallback;
802
803         function matchesByName(gridNode) {
804             return ("name" in gridNode) && gridNode.name.hasSubstring(query, true);
805         }
806
807         function matchesById(gridNode) {
808             return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query;
809         }
810
811         var matchPredicate;
812         if (query.charAt(0) !== "@")
813             matchPredicate = matchesByName;
814         else {
815             query = parseInt(query.substring(1), 10);
816             matchPredicate = matchesById;
817         }
818
819         function matchesQuery(gridNode)
820         {
821             delete gridNode._searchMatched;
822             if (matchPredicate(gridNode)) {
823                 gridNode._searchMatched = true;
824                 gridNode.refresh();
825                 return true;
826             }
827             return false;
828         }
829
830         var current = this.dataGrid.children[0];
831         var depth = 0;
832         var info = {};
833
834         // Restrict to type nodes and instances.
835         const maxDepth = 1;
836
837         while (current) {
838             if (matchesQuery(current))
839                 this._searchResults.push({ node: current });
840             current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
841             depth += info.depthChange;
842         }
843
844         finishedCallback(this, this._searchResults.length);
845     },
846
847     jumpToFirstSearchResult: function()
848     {
849         if (!this._searchResults || !this._searchResults.length)
850             return;
851         this._currentSearchResultIndex = 0;
852         this._jumpToSearchResult(this._currentSearchResultIndex);
853     },
854
855     jumpToLastSearchResult: function()
856     {
857         if (!this._searchResults || !this._searchResults.length)
858             return;
859         this._currentSearchResultIndex = (this._searchResults.length - 1);
860         this._jumpToSearchResult(this._currentSearchResultIndex);
861     },
862
863     jumpToNextSearchResult: function()
864     {
865         if (!this._searchResults || !this._searchResults.length)
866             return;
867         if (++this._currentSearchResultIndex >= this._searchResults.length)
868             this._currentSearchResultIndex = 0;
869         this._jumpToSearchResult(this._currentSearchResultIndex);
870     },
871
872     jumpToPreviousSearchResult: function()
873     {
874         if (!this._searchResults || !this._searchResults.length)
875             return;
876         if (--this._currentSearchResultIndex < 0)
877             this._currentSearchResultIndex = (this._searchResults.length - 1);
878         this._jumpToSearchResult(this._currentSearchResultIndex);
879     },
880
881     showingFirstSearchResult: function()
882     {
883         return (this._currentSearchResultIndex === 0);
884     },
885
886     showingLastSearchResult: function()
887     {
888         return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
889     },
890
891     _jumpToSearchResult: function(index)
892     {
893         var searchResult = this._searchResults[index];
894         if (!searchResult)
895             return;
896
897         var node = searchResult.node;
898         node.revealAndSelect();
899     },
900
901     refreshVisibleData: function()
902     {
903         var child = this.dataGrid.children[0];
904         while (child) {
905             child.refresh();
906             child = child.traverseNextNode(false, null, true);
907         }
908     },
909
910     _changeBase: function()
911     {
912         if (this._baseProfileUid === this._profiles()[this.baseSelectElement.selectedIndex].uid)
913             return;
914
915         this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid;
916         this.dataGrid._baseProfileIndexChanged(this._loadProfileByIndex.bind(this), this.baseSelectElement.selectedIndex);
917
918         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
919             return;
920
921         // The current search needs to be performed again. First negate out previous match
922         // count by calling the search finished callback with a negative number of matches.
923         // Then perform the search again with the same query and callback.
924         this._searchFinishedCallback(this, -this._searchResults.length);
925         this.performSearch(this.currentQuery, this._searchFinishedCallback);
926     },
927
928     _changeFilter: function()
929     {
930         var profileIndex = this.filterSelectElement.selectedIndex - 1;
931         this.dataGrid._filterSelectIndexChanged(this._loadProfileByIndex.bind(this), profileIndex);
932
933         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
934             return;
935
936         // The current search needs to be performed again. First negate out previous match
937         // count by calling the search finished callback with a negative number of matches.
938         // Then perform the search again with the same query and callback.
939         this._searchFinishedCallback(this, -this._searchResults.length);
940         this.performSearch(this.currentQuery, this._searchFinishedCallback);
941     },
942
943     _profiles: function()
944     {
945         return WebInspector.panels.profiles.getProfiles(WebInspector.DetailedHeapshotProfileType.TypeId);
946     },
947
948     _loadProfile: function(profileUid, callback)
949     {
950         WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
951     },
952
953     _loadProfileByIndex: function(profileIndex, callback)
954     {
955         var profileUid = this._profiles()[profileIndex].uid;
956         WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
957     },
958
959     isDetailedSnapshot: function(snapshot)
960     {
961         var s = new WebInspector.HeapSnapshot(snapshot);
962         for (var iter = s.rootNode.edges; iter.hasNext(); iter.next())
963             if (iter.edge.node.name === "(GC roots)")
964                 return true;
965         return false;
966     },
967
968     processLoadedSnapshot: function(profile, snapshot)
969     {
970         profile.nodes = snapshot.nodes;
971         profile.strings = snapshot.strings;
972         var s = new WebInspector.HeapSnapshot(profile);
973         profile.sidebarElement.subtitle = Number.bytesToString(s.totalSize);
974     },
975
976     _mouseClickInContentsGrid: function(event)
977     {
978         var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
979         if (!cell || (!cell.hasStyleClass("object-column")))
980             return;
981         var row = event.target.enclosingNodeOrSelfWithNodeName("tr");
982         if (!row)
983             return;
984         var nodeItem = row._dataGridNode;
985         if (!nodeItem || nodeItem.isEventWithinDisclosureTriangle(event))
986             return;
987         if (nodeItem.snapshotNodeIndex)
988             this.retainmentDataGrid.setDataSource(this, nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex, nodeItem.isDeletedNode ? this.baseSelectElement.childNodes[this.baseSelectElement.selectedIndex].label + " | " : "");
989         else
990             this.retainmentDataGrid.reset();
991     },
992
993     _mouseDownInContentsGrid: function(event)
994     {
995         if (event.detail < 2)
996             return;
997
998         var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
999         if (!cell || (!cell.hasStyleClass("count-column") && !cell.hasStyleClass("shallowSize-column") && !cell.hasStyleClass("retainedSize-column")))
1000             return;
1001
1002         if (cell.hasStyleClass("count-column"))
1003             this.showCountAsPercent = !this.showCountAsPercent;
1004         else if (cell.hasStyleClass("shallowSize-column"))
1005             this.showShallowSizeAsPercent = !this.showShallowSizeAsPercent;
1006         else if (cell.hasStyleClass("retainedSize-column"))
1007             this.showRetainedSizeAsPercent = !this.showRetainedSizeAsPercent;
1008         this.refreshShowAsPercents();
1009
1010         event.preventDefault();
1011         event.stopPropagation();
1012     },
1013
1014     _mouseClickInRetainmentGrid: function(event)
1015     {
1016         var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
1017         if (!cell || (!cell.hasStyleClass("path-column")))
1018             return;
1019         var row = event.target.enclosingNodeOrSelfWithNodeName("tr");
1020         var nodeItem = row._dataGridNode;
1021         if (!nodeItem || !nodeItem.route)
1022             return;
1023         function expandRoute()
1024         {
1025             this.dataGrid.expandRoute(nodeItem.route);
1026         }
1027         this.changeView("Containment", expandRoute.bind(this));
1028     },
1029
1030     changeView: function(viewTitle, callback)
1031     {
1032         var viewIndex = null;
1033         for (var i = 0; i < this.views.length; ++i)
1034             if (this.views[i].title === viewTitle) {
1035                 viewIndex = i;
1036                 break;
1037             }
1038         if (this.views.current === viewIndex) {
1039             setTimeout(callback, 0);
1040             return;
1041         }
1042         var grid = this.views[viewIndex].grid;
1043         function sortingComplete()
1044         {
1045             grid.removeEventListener("sorting complete", sortingComplete, this);
1046             setTimeout(callback, 0);
1047         }
1048         this.views[viewIndex].grid.addEventListener("sorting complete", sortingComplete, this);
1049         this.viewSelectElement.selectedIndex = viewIndex;
1050         this._changeView({target: {selectedIndex: viewIndex}});
1051     },
1052
1053     _changeView: function(event)
1054     {
1055         if (!event || !this._profileUid)
1056             return;
1057         if (event.target.selectedIndex === this.views.current)
1058             return;
1059
1060         this.views.current = event.target.selectedIndex;
1061         this.currentView.detach();
1062         var view = this.views[this.views.current];
1063         this.currentView = view.view;
1064         this.dataGrid = view.grid;
1065         this.currentView.show(this.viewsContainer);
1066         this.refreshVisibleData();
1067         this.dataGrid.updateWidths();
1068
1069         if (this.currentView === this.diffView) {
1070             this.baseSelectElement.removeStyleClass("hidden");
1071             if (!this.dataGrid.snapshotView) {
1072                 this._changeBase();
1073                 this.dataGrid.setDataSource(this, this.profileWrapper);
1074             }
1075         } else {
1076             this.baseSelectElement.addStyleClass("hidden");
1077             if (!this.dataGrid.snapshotView)
1078                 this.dataGrid.setDataSource(this, this.profileWrapper);
1079         }
1080
1081         if (this.currentView === this.constructorsView)
1082             this.filterSelectElement.removeStyleClass("hidden");
1083         else
1084             this.filterSelectElement.addStyleClass("hidden");
1085
1086         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
1087             return;
1088
1089         // The current search needs to be performed again. First negate out previous match
1090         // count by calling the search finished callback with a negative number of matches.
1091         // Then perform the search again the with same query and callback.
1092         this._searchFinishedCallback(this, -this._searchResults.length);
1093         this.performSearch(this.currentQuery, this._searchFinishedCallback);
1094     },
1095
1096     _changeRetainingPathsRoot: function(event)
1097     {
1098         if (!event)
1099             return;
1100         this.retainmentDataGrid.refresh();
1101     },
1102
1103     _getHoverAnchor: function(target)
1104     {
1105         var span = target.enclosingNodeOrSelfWithNodeName("span");
1106         if (!span)
1107             return;
1108         var row = target.enclosingNodeOrSelfWithNodeName("tr");
1109         if (!row)
1110             return;
1111         var gridNode = row._dataGridNode;
1112         if (!gridNode.hasHoverMessage)
1113             return;
1114         span.node = gridNode;
1115         return span;
1116     },
1117
1118     get isTracingToWindowObjects()
1119     {
1120         return this.retainingPathsRoot.selectedIndex === 1;
1121     },
1122
1123     get _isShowingAsPercent()
1124     {
1125         return this.showCountAsPercent && this.showShallowSizeAsPercent && this.showRetainedSizeAsPercent;
1126     },
1127
1128     _percentClicked: function(event)
1129     {
1130         var currentState = this._isShowingAsPercent;
1131         this.showCountAsPercent = !currentState;
1132         this.showShallowSizeAsPercent = !currentState;
1133         this.showRetainedSizeAsPercent = !currentState;
1134         this.refreshShowAsPercents();
1135     },
1136
1137     _showObjectPopover: function(element, showCallback)
1138     {
1139         element.node.queryObjectContent(showCallback);
1140     },
1141
1142     _helpClicked: function(event)
1143     {
1144         if (!this._helpPopoverContentElement) {
1145             var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"),
1146                             "0:", "console-formatted-name", WebInspector.UIString("element"),
1147                             "a:", "console-formatted-number", WebInspector.UIString("context var"),
1148                             "a:", "console-formatted-null", WebInspector.UIString("system prop")];
1149             var objTypes = [" a ", "console-formatted-object", "Object",
1150                             "\"a\"", "console-formatted-string", "String",
1151                             "/a/", "console-formatted-string", "RegExp",
1152                             "a()", "console-formatted-function", "Function",
1153                             "a[]", "console-formatted-object", "Array",
1154                             "num", "console-formatted-number", "Number",
1155                             " a ", "console-formatted-null", "System"];
1156
1157             var contentElement = document.createElement("table");
1158             contentElement.className = "heapshot-help";
1159             var headerRow = document.createElement("tr");
1160             var propsHeader = document.createElement("th");
1161             propsHeader.textContent = WebInspector.UIString("Property types:");
1162             headerRow.appendChild(propsHeader);
1163             var objsHeader = document.createElement("th");
1164             objsHeader.textContent = WebInspector.UIString("Object types:");
1165             headerRow.appendChild(objsHeader);
1166             contentElement.appendChild(headerRow);
1167             var len = Math.max(refTypes.length, objTypes.length);
1168             for (var i = 0; i < len; i += 3) {
1169                 var row = document.createElement("tr");
1170                 var refCell = document.createElement("td");
1171                 if (refTypes[i])
1172                     appendHelp(refTypes, i, refCell);
1173                 row.appendChild(refCell);
1174                 var objCell = document.createElement("td");
1175                 if (objTypes[i])
1176                     appendHelp(objTypes, i, objCell);
1177                 row.appendChild(objCell);
1178                 contentElement.appendChild(row);
1179             }
1180             this._helpPopoverContentElement = contentElement;
1181             this.helpPopover = new WebInspector.Popover();
1182
1183             function appendHelp(help, index, cell)
1184             {
1185                 var div = document.createElement("div");
1186                 div.className = "source-code event-properties";
1187                 var name = document.createElement("span");
1188                 name.textContent = help[index];
1189                 name.className = help[index + 1];
1190                 div.appendChild(name);
1191                 var desc = document.createElement("span");
1192                 desc.textContent = " " + help[index + 2];
1193                 div.appendChild(desc);
1194                 cell.appendChild(div);
1195             }
1196         }
1197         if (this.helpPopover.visible)
1198             this.helpPopover.hide();
1199         else
1200             this.helpPopover.show(this._helpPopoverContentElement, this.helpButton.element);
1201     },
1202
1203     _startRetainersHeaderDragging: function(event)
1204     {
1205         if (!this.isShowing() || event.target === this.retainingPathsRoot)
1206             return;
1207
1208         WebInspector.elementDragStart(this.retainmentViewHeader, this._retainersHeaderDragging.bind(this), this._endRetainersHeaderDragging.bind(this), event, "row-resize");
1209         this._previousDragPosition = event.pageY;
1210         event.stopPropagation();
1211     },
1212
1213     _retainersHeaderDragging: function(event)
1214     {
1215         var height = this.retainmentView.element.clientHeight;
1216         height += this._previousDragPosition - event.pageY;
1217         this._previousDragPosition = event.pageY;
1218         this._updateRetainmentViewHeight(height);
1219         event.preventDefault();
1220         event.stopPropagation();
1221     },
1222
1223     _endRetainersHeaderDragging: function(event)
1224     {
1225         WebInspector.elementDragEnd(event);
1226         delete this._previousDragPosition;
1227         event.stopPropagation();
1228     },
1229
1230     _updateRetainmentViewHeight: function(height)
1231     {
1232         height = Number.constrain(height, Preferences.minConsoleHeight, this.element.clientHeight - Preferences.minConsoleHeight);
1233         this.viewsContainer.style.bottom = (height + this.retainmentViewHeader.clientHeight) + "px";
1234         this.retainmentView.element.style.height = height + "px";
1235         this.retainmentViewHeader.style.bottom = height + "px";
1236     },
1237
1238     _updateBaseOptions: function()
1239     {
1240         var list = this._profiles();
1241         // We're assuming that snapshots can only be added.
1242         if (this.baseSelectElement.length === list.length)
1243             return;
1244
1245         for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) {
1246             var baseOption = document.createElement("option");
1247             var title = list[i].title;
1248             if (!title.indexOf(UserInitiatedProfileName))
1249                 title = WebInspector.UIString("Snapshot %d", title.substring(UserInitiatedProfileName.length + 1));
1250             baseOption.label = title;
1251             this.baseSelectElement.appendChild(baseOption);
1252         }
1253     },
1254
1255     _updateFilterOptions: function()
1256     {
1257         var list = this._profiles();
1258         // We're assuming that snapshots can only be added.
1259         if (this.filterSelectElement.length - 1 === list.length)
1260             return;
1261
1262         if (!this.filterSelectElement.length) {
1263             var filterOption = document.createElement("option");
1264             filterOption.label = WebInspector.UIString("All objects");
1265             this.filterSelectElement.appendChild(filterOption);
1266         }
1267
1268         for (var i = this.filterSelectElement.length - 1, n = list.length; i < n; ++i) {
1269             var filterOption = document.createElement("option");
1270             var title = list[i].title;
1271             if (!title.indexOf(UserInitiatedProfileName)) {
1272                 if (!i)
1273                     title = WebInspector.UIString("Objects allocated before Snapshot %d", title.substring(UserInitiatedProfileName.length + 1));
1274                 else
1275                     title = WebInspector.UIString("Objects allocated between Snapshots %d and %d", title.substring(UserInitiatedProfileName.length + 1) - 1, title.substring(UserInitiatedProfileName.length + 1));
1276             }
1277             filterOption.label = title;
1278             this.filterSelectElement.appendChild(filterOption);
1279         }
1280     },
1281
1282     _updatePercentButton: function()
1283     {
1284         if (this._isShowingAsPercent) {
1285             this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes.");
1286             this.percentButton.toggled = true;
1287         } else {
1288             this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages.");
1289             this.percentButton.toggled = false;
1290         }
1291     }
1292 };
1293
1294 WebInspector.DetailedHeapshotView.prototype.__proto__ = WebInspector.View.prototype;
1295
1296 WebInspector.DetailedHeapshotView.prototype.showHiddenData = true;
1297
1298 WebInspector.DetailedHeapshotProfileType = function()
1299 {
1300     WebInspector.ProfileType.call(this, WebInspector.DetailedHeapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS"));
1301 }
1302
1303 WebInspector.DetailedHeapshotProfileType.TypeId = "HEAP";
1304
1305 WebInspector.DetailedHeapshotProfileType.prototype = {
1306     get buttonTooltip()
1307     {
1308         return WebInspector.UIString("Take heap snapshot.");
1309     },
1310
1311     get buttonStyle()
1312     {
1313         return "heap-snapshot-status-bar-item status-bar-item";
1314     },
1315
1316     buttonClicked: function()
1317     {
1318         WebInspector.panels.profiles.takeHeapSnapshot();
1319     },
1320
1321     get welcomeMessage()
1322     {
1323         return WebInspector.UIString("Get a heap snapshot by pressing the %s button on the status bar.");
1324     },
1325
1326     createSidebarTreeElementForProfile: function(profile)
1327     {
1328         return new WebInspector.ProfileSidebarTreeElement(profile, WebInspector.UIString("Snapshot %d"), "heap-snapshot-sidebar-tree-item");
1329     },
1330
1331     createView: function(profile)
1332     {
1333         return new WebInspector.DetailedHeapshotView(WebInspector.panels.profiles, profile);
1334     }
1335 }
1336
1337 WebInspector.DetailedHeapshotProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype;