2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
31 WebInspector.HeapSnapshotSortableDataGrid = function(columns)
33 WebInspector.DataGrid.call(this, columns);
34 this.addEventListener("sorting changed", this.sortingChanged, this);
37 WebInspector.HeapSnapshotSortableDataGrid.prototype = {
40 for (var i = 0, l = this.children.length; i < l; ++i)
41 this.children[i].dispose();
44 resetSortingCache: function()
46 delete this._lastSortColumnIdentifier;
47 delete this._lastSortAscending;
50 sortingChanged: function()
52 var sortAscending = this.sortOrder === "ascending";
53 var sortColumnIdentifier = this.sortColumnIdentifier;
54 if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
56 this._lastSortColumnIdentifier = sortColumnIdentifier;
57 this._lastSortAscending = sortAscending;
58 var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
60 function SortByTwoFields(nodeA, nodeB)
62 var field1 = nodeA[sortFields[0]];
63 var field2 = nodeB[sortFields[0]];
64 var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
69 field1 = nodeA[sortFields[2]];
70 field2 = nodeB[sortFields[2]];
71 result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
76 this._performSorting(SortByTwoFields);
79 _performSorting: function(sortFunction)
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);
91 this.recursiveSortingLeave();
94 recursiveSortingEnter: function()
96 if (!("_recursiveSortingDepth" in this))
97 this._recursiveSortingDepth = 1;
99 ++this._recursiveSortingDepth;
102 recursiveSortingLeave: function()
104 if (!("_recursiveSortingDepth" in this))
106 if (!--this._recursiveSortingDepth) {
107 delete this._recursiveSortingDepth;
108 this.dispatchEventToListeners("sorting complete");
113 WebInspector.HeapSnapshotSortableDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype;
115 WebInspector.HeapSnapshotContainmentDataGrid = function()
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 }
122 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
125 WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
126 _defaultPopulateCount: 100,
128 expandRoute: function(route)
130 function nextStep(parent, hopIndex)
132 if (hopIndex >= route.length) {
133 parent.element.scrollIntoViewIfNeeded(true);
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) {
142 nextStep(child, hopIndex + 1);
144 function afterExpand()
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);
151 child.addEventListener("populate complete", afterExpand, null);
152 lastChild.showAll.click();
155 child.addEventListener("populate complete", afterExpand, null);
165 setDataSource: function(snapshotView, snapshot)
167 this.snapshotView = snapshotView;
168 this.snapshot = snapshot;
169 this.snapshotNodeIndex = this.snapshot.rootNodeIndex;
170 this._provider = this._createProvider(snapshot, this.snapshotNodeIndex);
174 sortingChanged: function()
180 MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotObjectNode.prototype, WebInspector.HeapSnapshotContainmentDataGrid.prototype);
181 WebInspector.HeapSnapshotContainmentDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
183 WebInspector.HeapSnapshotConstructorsDataGrid = function()
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 }
191 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
194 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
195 _defaultPopulateCount: 100,
197 _sortFields: function(sortColumn, sortAscending)
200 object: ["_name", sortAscending, "_count", false],
201 count: ["_count", sortAscending, "_name", true],
202 shallowSize: ["_shallowSize", sortAscending, "_name", true],
203 retainedSize: ["_retainedSize", sortAscending, "_name", true]
207 setDataSource: function(snapshotView, snapshot)
209 this.snapshotView = snapshotView;
210 this.snapshot = snapshot;
211 this.populateChildren();
214 populateChildren: function()
216 function aggregatesReceived(aggregates)
218 for (var constructor in aggregates)
219 this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor]));
220 this.sortingChanged();
222 this.snapshot.aggregates(false, aggregatesReceived.bind(this));
226 WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
228 WebInspector.HeapSnapshotDiffDataGrid = function()
231 object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true },
232 addedCount: { title: WebInspector.UIString("# New"), width: "72px", sortable: true, sort: "descending" },
233 removedCount: { title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true },
234 // \u0394 is a Greek delta letter.
235 countDelta: { title: "\u0394", width: "40px", sortable: true },
236 addedSize: { title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true },
237 removedSize: { title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true },
238 sizeDelta: { title: "\u0394", width: "72px", sortable: true }
240 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
243 WebInspector.HeapSnapshotDiffDataGrid.prototype = {
244 _defaultPopulateCount: 50,
246 _sortFields: function(sortColumn, sortAscending)
249 object: ["_name", sortAscending, "_count", false],
250 addedCount: ["_addedCount", sortAscending, "_name", true],
251 removedCount: ["_removedCount", sortAscending, "_name", true],
252 countDelta: ["_countDelta", sortAscending, "_name", true],
253 addedSize: ["_addedSize", sortAscending, "_name", true],
254 removedSize: ["_removedSize", sortAscending, "_name", true],
255 sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
259 setDataSource: function(snapshotView, snapshot)
261 this.snapshotView = snapshotView;
262 this.snapshot = snapshot;
265 setBaseDataSource: function(baseSnapshot)
267 this.baseSnapshot = baseSnapshot;
269 this.removeChildren();
270 this.resetSortingCache();
271 if (this.baseSnapshot === this.snapshot) {
272 this.dispatchEventToListeners("sorting complete");
275 this.populateChildren();
278 populateChildren: function()
280 function baseAggregatesReceived(baseClasses)
282 function aggregatesReceived(classes)
286 for (var clss in baseClasses)
287 nodes.push(new WebInspector.HeapSnapshotDiffNode(this, clss, baseClasses[clss], classes[clss]));
288 for (clss in classes) {
289 if (!(clss in baseClasses))
290 nodes.push(new WebInspector.HeapSnapshotDiffNode(this, clss, null, classes[clss]));
292 nodeCount = nodes.length;
293 function addNodeIfNonZeroDiff(boundNode, zeroDiff)
296 this.appendChild(boundNode);
298 this.sortingChanged();
300 for (var i = 0, l = nodes.length; i < l; ++i) {
302 node.calculateDiff(this, addNodeIfNonZeroDiff.bind(this, node));
305 this.snapshot.aggregates(true, aggregatesReceived.bind(this));
307 this.baseSnapshot.aggregates(true, baseAggregatesReceived.bind(this));
311 WebInspector.HeapSnapshotDiffDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
313 WebInspector.HeapSnapshotDominatorsDataGrid = function()
316 object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true },
317 shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true },
318 retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
320 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
323 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
324 _defaultPopulateCount: 25,
326 setDataSource: function(snapshotView, snapshot)
328 this.snapshotView = snapshotView;
329 this.snapshot = snapshot;
330 this.snapshotNodeIndex = this.snapshot.rootNodeIndex;
331 this._provider = this._createProvider(snapshot, this.snapshotNodeIndex);
335 sortingChanged: function()
341 MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotDominatorObjectNode.prototype, WebInspector.HeapSnapshotDominatorsDataGrid.prototype);
342 WebInspector.HeapSnapshotDominatorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
344 WebInspector.HeapSnapshotPathFinderState = function(snapshot, nodeIndex, rootFilter)
346 this._pathFinder = snapshot.createPathFinder(nodeIndex, !WebInspector.DetailedHeapshotView.prototype.showHiddenData);
347 this._pathFinder.updateRoots(rootFilter);
348 this._foundCount = 0;
349 this._foundCountMax = null;
350 this._totalFoundCount = 0;
351 this._cancelled = false;
354 WebInspector.HeapSnapshotPathFinderState.prototype = {
355 batchDone: function(status)
359 pathFound: function(path)
365 this._cancelled = true;
366 this._pathFinder.dispose();
369 startBatch: function(count)
373 this._foundCount = 0;
374 this._foundCountMax = count;
375 this._pathFinder.findNext(this._pathFound.bind(this));
378 _pathFound: function(result)
382 if (result === null) {
383 if (!this._totalFoundCount)
384 this.batchDone("no-paths-at-all");
385 } else if (result !== false) {
386 this.pathFound(result);
388 ++this._totalFoundCount;
389 if (this._foundCount < this._foundCountMax)
390 this._pathFinder.findNext(this._pathFound.bind(this));
392 this.batchDone("have-more-paths");
394 this.batchDone("no-more-paths");
399 WebInspector.HeapSnapshotRetainingPathsList = function()
402 path: { title: WebInspector.UIString("Retaining path"), sortable: true },
403 len: { title: WebInspector.UIString("Length"), width: "90px", sortable: true, sort: "ascending" }
405 WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
406 this._defaultPopulateCount = 100;
407 this._nodeIndex = null;
412 WebInspector.HeapSnapshotRetainingPathsList.prototype = {
416 this._state.cancel();
419 _sortFields: function(sortColumn, sortAscending)
422 path: ["path", sortAscending, "len", true],
423 len: ["len", sortAscending, "path", true]
427 _resetPaths: function()
429 var rootFilter = this.snapshotView.isTracingToWindowObjects ?
430 "function (node) { return node.name.substr(0, 9) === \"DOMWindow\"; }" : null;
432 this._state.cancel();
433 this._state = new WebInspector.HeapSnapshotPathFinderState(this._snapshot, this._nodeIndex, rootFilter);
434 this._state.batchDone = this._batchDone.bind(this);
435 this._state.pathFound = this._pathFound.bind(this);
436 this.removeChildren();
437 this.resetSortingCache();
438 this.showNext(this._defaultPopulateCount);
441 setDataSource: function(snapshotView, snapshot, nodeIndex, prefix)
443 if (this._nodeIndex === nodeIndex)
445 this.snapshotView = snapshotView;
446 this._snapshot = snapshot;
447 this._nodeIndex = nodeIndex;
448 this._prefix = prefix;
454 if (this.snapshotView)
461 this._state.cancel();
462 this.removeChildren();
463 this.resetSortingCache();
464 this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("Click on an object to show retaining paths"), len:""}, false));
467 _batchDone: function(state)
470 case "no-paths-at-all":
471 this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("Can't find any paths."), len:""}, false));
473 case "have-more-paths":
474 this.appendChild(new WebInspector.ShowMoreDataGridNode(this.showNext.bind(this), this._defaultPopulateCount));
475 this.resetSortingCache();
476 this.sortingChanged();
478 case "no-more-paths":
484 _pathFound: function(result)
486 if (WebInspector.HeapSnapshotGenericObjectNode.prototype.isDOMWindow(result.path))
487 result.path = WebInspector.HeapSnapshotGenericObjectNode.prototype.shortenWindowURL(result.path, true);
489 result.path = this._prefix + result.path;
490 var node = new WebInspector.DataGridNode(result, false);
491 node.route = result.route;
492 this.appendChild(node);
495 showNext: function(pathsCount)
497 this._state.startBatch(pathsCount);
500 _performSorting: function(sortFunction)
502 function DataExtractorWrapper(nodeA, nodeB)
504 return sortFunction(nodeA.data, nodeB.data);
506 this.recursiveSortingEnter();
507 this.sortNodes(DataExtractorWrapper);
508 this.recursiveSortingLeave();
512 WebInspector.HeapSnapshotRetainingPathsList.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
514 WebInspector.DetailedHeapshotView = function(parent, profile)
516 WebInspector.View.call(this);
518 this.element.addStyleClass("detailed-heapshot-view");
520 this.parent = parent;
521 this.parent.addEventListener("profile added", this._updateBaseOptions, this);
523 this.showCountAsPercent = false;
524 this.showShallowSizeAsPercent = false;
525 this.showRetainedSizeAsPercent = false;
527 this.viewsContainer = document.createElement("div");
528 this.viewsContainer.addStyleClass("views-container");
529 this.element.appendChild(this.viewsContainer);
531 this.containmentView = new WebInspector.View();
532 this.containmentView.element.addStyleClass("view");
533 this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
534 this.containmentDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
535 this.containmentDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
536 this.containmentView.element.appendChild(this.containmentDataGrid.element);
537 this.viewsContainer.appendChild(this.containmentView.element);
539 this.constructorsView = new WebInspector.View();
540 this.constructorsView.element.addStyleClass("view");
541 this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
542 this.constructorsDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
543 this.constructorsDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
544 this.constructorsView.element.appendChild(this.constructorsDataGrid.element);
545 this.viewsContainer.appendChild(this.constructorsView.element);
547 this.diffView = new WebInspector.View();
548 this.diffView.element.addStyleClass("view");
549 this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
550 this.diffDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
551 this.diffView.element.appendChild(this.diffDataGrid.element);
552 this.viewsContainer.appendChild(this.diffView.element);
554 this.dominatorView = new WebInspector.View();
555 this.dominatorView.element.addStyleClass("view");
556 this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
557 this.dominatorDataGrid.element.addEventListener("click", this._mouseClickInContentsGrid.bind(this), true);
558 this.dominatorDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
559 this.dominatorView.element.appendChild(this.dominatorDataGrid.element);
560 this.viewsContainer.appendChild(this.dominatorView.element);
562 this.retainmentViewHeader = document.createElement("div");
563 this.retainmentViewHeader.addStyleClass("retainers-view-header");
564 this.retainmentViewHeader.addEventListener("mousedown", this._startRetainersHeaderDragging.bind(this), true);
565 var retainingPathsTitleDiv = document.createElement("div");
566 retainingPathsTitleDiv.className = "title";
567 var retainingPathsTitle = document.createElement("span");
568 retainingPathsTitle.textContent = WebInspector.UIString("Paths from the selected object");
569 this.retainingPathsRoot = document.createElement("select");
570 this.retainingPathsRoot.className = "status-bar-item";
571 this.retainingPathsRoot.addEventListener("change", this._changeRetainingPathsRoot.bind(this), false);
572 var toGCRootsTraceOption = document.createElement("option");
573 toGCRootsTraceOption.label = WebInspector.UIString("to GC roots");
574 var toWindowObjectsTraceOption = document.createElement("option");
575 toWindowObjectsTraceOption.label = WebInspector.UIString("to window objects");
576 this.retainingPathsRoot.appendChild(toGCRootsTraceOption);
577 this.retainingPathsRoot.appendChild(toWindowObjectsTraceOption);
578 retainingPathsTitleDiv.appendChild(retainingPathsTitle);
579 retainingPathsTitleDiv.appendChild(this.retainingPathsRoot);
580 this.retainmentViewHeader.appendChild(retainingPathsTitleDiv);
581 this.element.appendChild(this.retainmentViewHeader);
583 this.retainmentView = new WebInspector.View();
584 this.retainmentView.element.addStyleClass("view");
585 this.retainmentView.element.addStyleClass("retaining-paths-view");
586 this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainingPathsList();
587 this.retainmentDataGrid.element.addEventListener("click", this._mouseClickInRetainmentGrid.bind(this), true);
588 this.retainmentView.element.appendChild(this.retainmentDataGrid.element);
589 this.retainmentView.visible = true;
590 this.element.appendChild(this.retainmentView.element);
591 this.retainmentDataGrid.reset();
593 this.dataGrid = this.constructorsDataGrid;
594 this.currentView = this.constructorsView;
596 this.viewSelectElement = document.createElement("select");
597 this.viewSelectElement.className = "status-bar-item";
598 this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false);
600 this.views = [{title: "Summary", view: this.constructorsView, grid: this.constructorsDataGrid},
601 {title: "Comparison", view: this.diffView, grid: this.diffDataGrid},
602 {title: "Containment", view: this.containmentView, grid: this.containmentDataGrid},
603 {title: "Dominators", view: this.dominatorView, grid: this.dominatorDataGrid}];
604 this.views.current = 0;
605 for (var i = 0; i < this.views.length; ++i) {
606 var view = this.views[i];
607 var option = document.createElement("option");
608 option.label = WebInspector.UIString(view.title);
609 this.viewSelectElement.appendChild(option);
612 this._profileUid = profile.uid;
614 this.baseSelectElement = document.createElement("select");
615 this.baseSelectElement.className = "status-bar-item hidden";
616 this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false);
617 this._updateBaseOptions();
619 this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item");
620 this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
621 this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item");
622 this.helpButton.addEventListener("click", this._helpClicked.bind(this), false);
624 var popoverHelper = new WebInspector.PopoverHelper(this.element, this._getHoverAnchor.bind(this), this._showStringContentPopover.bind(this));
626 this._loadProfile(this._profileUid, profileCallback.bind(this));
628 function profileCallback()
630 var list = this._profiles();
632 for (var i = 0; i < list.length; ++i)
633 if (list[i].uid === this._profileUid) {
637 if (profileIndex > 0)
638 this.baseSelectElement.selectedIndex = profileIndex - 1;
640 this.baseSelectElement.selectedIndex = profileIndex;
641 this.dataGrid.setDataSource(this, this.profileWrapper);
642 this._updatePercentButton();
646 WebInspector.DetailedHeapshotView.prototype = {
649 this.profileWrapper.dispose();
650 if (this.baseProfile)
651 this.baseProfileWrapper.dispose();
652 this.containmentDataGrid.dispose();
653 this.constructorsDataGrid.dispose();
654 this.diffDataGrid.dispose();
655 this.dominatorDataGrid.dispose();
656 this.retainmentDataGrid.dispose();
661 return [this.viewSelectElement, this.baseSelectElement, this.percentButton.element, this.helpButton.element];
666 return this.parent.getProfile(WebInspector.DetailedHeapshotProfileType.TypeId, this._profileUid);
671 return this.profile.proxy;
676 return this.parent.getProfile(WebInspector.DetailedHeapshotProfileType.TypeId, this._baseProfileUid);
679 get baseProfileWrapper()
681 return this.baseProfile.proxy;
684 show: function(parentElement)
686 WebInspector.View.prototype.show.call(this, parentElement);
687 if (!this.profileWrapper.loaded)
688 this._loadProfile(this._profileUid, profileCallback1.bind(this));
690 profileCallback1.call(this);
692 function profileCallback1() {
693 if (this.baseProfile && !this.baseProfileWrapper.loaded)
694 this._loadProfile(this._baseProfileUid, profileCallback2.bind(this));
696 profileCallback2.call(this);
699 function profileCallback2() {
700 this.currentView.show();
701 this.dataGrid.updateWidths();
707 WebInspector.View.prototype.hide.call(this);
708 this._currentSearchResultIndex = -1;
714 this.dataGrid.updateWidths();
716 var height = this.retainmentView.element.clientHeight;
717 this._updateRetainmentViewHeight(height);
720 refreshShowAsPercents: function()
722 this._updatePercentButton();
723 this.refreshVisibleData();
726 searchCanceled: function()
728 if (this._searchResults) {
729 for (var i = 0; i < this._searchResults.length; ++i) {
730 var node = this._searchResults[i].node;
731 delete node._searchMatched;
736 delete this._searchFinishedCallback;
737 this._currentSearchResultIndex = -1;
738 this._searchResults = [];
741 performSearch: function(query, finishedCallback)
743 // Call searchCanceled since it will reset everything we need before doing a new search.
744 this.searchCanceled();
746 query = query.trim();
750 if (this.currentView !== this.constructorsView && this.currentView !== this.diffView)
753 this._searchFinishedCallback = finishedCallback;
755 function matchesByName(gridNode) {
756 return ("name" in gridNode) && gridNode.name.hasSubstring(query, true);
759 function matchesById(gridNode) {
760 return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query;
764 if (query.charAt(0) !== "@")
765 matchPredicate = matchesByName;
767 query = parseInt(query.substring(1), 10);
768 matchPredicate = matchesById;
771 function matchesQuery(gridNode)
773 delete gridNode._searchMatched;
774 if (matchPredicate(gridNode)) {
775 gridNode._searchMatched = true;
782 var current = this.dataGrid.children[0];
786 // Restrict to type nodes and instances.
790 if (matchesQuery(current))
791 this._searchResults.push({ node: current });
792 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
793 depth += info.depthChange;
796 finishedCallback(this, this._searchResults.length);
799 jumpToFirstSearchResult: function()
801 if (!this._searchResults || !this._searchResults.length)
803 this._currentSearchResultIndex = 0;
804 this._jumpToSearchResult(this._currentSearchResultIndex);
807 jumpToLastSearchResult: function()
809 if (!this._searchResults || !this._searchResults.length)
811 this._currentSearchResultIndex = (this._searchResults.length - 1);
812 this._jumpToSearchResult(this._currentSearchResultIndex);
815 jumpToNextSearchResult: function()
817 if (!this._searchResults || !this._searchResults.length)
819 if (++this._currentSearchResultIndex >= this._searchResults.length)
820 this._currentSearchResultIndex = 0;
821 this._jumpToSearchResult(this._currentSearchResultIndex);
824 jumpToPreviousSearchResult: function()
826 if (!this._searchResults || !this._searchResults.length)
828 if (--this._currentSearchResultIndex < 0)
829 this._currentSearchResultIndex = (this._searchResults.length - 1);
830 this._jumpToSearchResult(this._currentSearchResultIndex);
833 showingFirstSearchResult: function()
835 return (this._currentSearchResultIndex === 0);
838 showingLastSearchResult: function()
840 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
843 _jumpToSearchResult: function(index)
845 var searchResult = this._searchResults[index];
849 var node = searchResult.node;
850 node.revealAndSelect();
853 refreshVisibleData: function()
855 var child = this.dataGrid.children[0];
858 child = child.traverseNextNode(false, null, true);
862 _changeBase: function()
864 if (this._baseProfileUid === this._profiles()[this.baseSelectElement.selectedIndex].uid)
867 this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid;
868 this._loadProfile(this._baseProfileUid, baseProfileLoaded.bind(this));
870 function baseProfileLoaded()
872 delete this._baseProfileWrapper;
873 this.baseProfile._lastShown = Date.now();
874 this.diffDataGrid.setBaseDataSource(this.baseProfileWrapper);
877 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
880 // The current search needs to be performed again. First negate out previous match
881 // count by calling the search finished callback with a negative number of matches.
882 // Then perform the search again with the same query and callback.
883 this._searchFinishedCallback(this, -this._searchResults.length);
884 this.performSearch(this.currentQuery, this._searchFinishedCallback);
887 _profiles: function()
889 return WebInspector.panels.profiles.getProfiles(WebInspector.DetailedHeapshotProfileType.TypeId);
892 _loadProfile: function(profileUid, callback)
894 WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
897 isDetailedSnapshot: function(snapshot)
899 var s = new WebInspector.HeapSnapshot(snapshot);
900 for (var iter = s.rootNode.edges; iter.hasNext(); iter.next())
901 if (iter.edge.node.name === "(GC roots)")
906 processLoadedSnapshot: function(profile, snapshot)
908 profile.nodes = snapshot.nodes;
909 profile.strings = snapshot.strings;
910 var s = new WebInspector.HeapSnapshot(profile);
911 profile.sidebarElement.subtitle = Number.bytesToString(s.totalSize);
914 _mouseClickInContentsGrid: function(event)
916 var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
917 if (!cell || (!cell.hasStyleClass("object-column")))
919 var row = event.target.enclosingNodeOrSelfWithNodeName("tr");
922 var nodeItem = row._dataGridNode;
923 if (!nodeItem || nodeItem.isEventWithinDisclosureTriangle(event))
925 if (nodeItem.snapshotNodeIndex)
926 this.retainmentDataGrid.setDataSource(this, nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex, nodeItem.isDeletedNode ? this.baseSelectElement.childNodes[this.baseSelectElement.selectedIndex].label + " | " : "");
928 this.retainmentDataGrid.reset();
931 _mouseDownInContentsGrid: function(event)
933 if (event.detail < 2)
936 var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
937 if (!cell || (!cell.hasStyleClass("count-column") && !cell.hasStyleClass("shallowSize-column") && !cell.hasStyleClass("retainedSize-column")))
940 if (cell.hasStyleClass("count-column"))
941 this.showCountAsPercent = !this.showCountAsPercent;
942 else if (cell.hasStyleClass("shallowSize-column"))
943 this.showShallowSizeAsPercent = !this.showShallowSizeAsPercent;
944 else if (cell.hasStyleClass("retainedSize-column"))
945 this.showRetainedSizeAsPercent = !this.showRetainedSizeAsPercent;
946 this.refreshShowAsPercents();
948 event.preventDefault();
949 event.stopPropagation();
952 _mouseClickInRetainmentGrid: function(event)
954 var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
955 if (!cell || (!cell.hasStyleClass("path-column")))
957 var row = event.target.enclosingNodeOrSelfWithNodeName("tr");
958 var nodeItem = row._dataGridNode;
959 if (!nodeItem || !nodeItem.route)
961 function expandRoute()
963 this.dataGrid.expandRoute(nodeItem.route);
965 this.changeView("Containment", expandRoute.bind(this));
968 changeView: function(viewTitle, callback)
970 var viewIndex = null;
971 for (var i = 0; i < this.views.length; ++i)
972 if (this.views[i].title === viewTitle) {
976 if (this.views.current === viewIndex) {
977 setTimeout(callback, 0);
980 var grid = this.views[viewIndex].grid;
981 function sortingComplete()
983 grid.removeEventListener("sorting complete", sortingComplete, this);
984 setTimeout(callback, 0);
986 this.views[viewIndex].grid.addEventListener("sorting complete", sortingComplete, this);
987 this.viewSelectElement.selectedIndex = viewIndex;
988 this._changeView({target: {selectedIndex: viewIndex}});
991 _changeView: function(event)
993 if (!event || !this._profileUid)
995 if (event.target.selectedIndex === this.views.current)
998 this.views.current = event.target.selectedIndex;
999 this.currentView.hide();
1000 var view = this.views[this.views.current];
1001 this.currentView = view.view;
1002 this.dataGrid = view.grid;
1003 this.currentView.show();
1004 this.refreshVisibleData();
1005 this.dataGrid.updateWidths();
1006 if (this.currentView === this.diffView) {
1007 this.baseSelectElement.removeStyleClass("hidden");
1008 if (!this.dataGrid.snapshotView) {
1009 this.dataGrid.setDataSource(this, this.profileWrapper);
1013 this.baseSelectElement.addStyleClass("hidden");
1014 if (!this.dataGrid.snapshotView)
1015 this.dataGrid.setDataSource(this, this.profileWrapper);
1018 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
1021 // The current search needs to be performed again. First negate out previous match
1022 // count by calling the search finished callback with a negative number of matches.
1023 // Then perform the search again the with same query and callback.
1024 this._searchFinishedCallback(this, -this._searchResults.length);
1025 this.performSearch(this.currentQuery, this._searchFinishedCallback);
1028 _changeRetainingPathsRoot: function(event)
1032 this.retainmentDataGrid.refresh();
1035 _getHoverAnchor: function(target)
1037 var span = target.enclosingNodeOrSelfWithNodeName("span");
1040 var row = target.enclosingNodeOrSelfWithNodeName("tr");
1043 var gridNode = row._dataGridNode;
1044 if (!gridNode.hasHoverMessage)
1046 span.node = gridNode;
1050 get isTracingToWindowObjects()
1052 return this.retainingPathsRoot.selectedIndex === 1;
1055 get _isShowingAsPercent()
1057 return this.showCountAsPercent && this.showShallowSizeAsPercent && this.showRetainedSizeAsPercent;
1060 _percentClicked: function(event)
1062 var currentState = this._isShowingAsPercent;
1063 this.showCountAsPercent = !currentState;
1064 this.showShallowSizeAsPercent = !currentState;
1065 this.showRetainedSizeAsPercent = !currentState;
1066 this.refreshShowAsPercents();
1069 _showStringContentPopover: function(anchor, popover)
1071 var stringContentElement = document.createElement("span");
1072 stringContentElement.className = "monospace";
1073 stringContentElement.style.whiteSpace = "pre";
1075 function displayString(name, className)
1077 stringContentElement.textContent = name;
1078 stringContentElement.className += " " + className;
1079 popover.show(stringContentElement, anchor);
1081 anchor.node.hoverMessage(displayString);
1084 _helpClicked: function(event)
1086 if (!this.helpPopover) {
1087 var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"),
1088 "0:", "console-formatted-name", WebInspector.UIString("element"),
1089 "a:", "console-formatted-number", WebInspector.UIString("context var"),
1090 "a:", "console-formatted-null", WebInspector.UIString("system prop")];
1091 var objTypes = [" a ", "console-formatted-object", "Object",
1092 "\"a\"", "console-formatted-string", "String",
1093 "/a/", "console-formatted-string", "RegExp",
1094 "a()", "console-formatted-function", "Function",
1095 "a[]", "console-formatted-object", "Array",
1096 "num", "console-formatted-number", "Number",
1097 " a ", "console-formatted-null", "System"];
1099 var contentElement = document.createElement("table");
1100 contentElement.className = "heapshot-help";
1101 var headerRow = document.createElement("tr");
1102 var propsHeader = document.createElement("th");
1103 propsHeader.textContent = WebInspector.UIString("Property types:");
1104 headerRow.appendChild(propsHeader);
1105 var objsHeader = document.createElement("th");
1106 objsHeader.textContent = WebInspector.UIString("Object types:");
1107 headerRow.appendChild(objsHeader);
1108 contentElement.appendChild(headerRow);
1109 var len = Math.max(refTypes.length, objTypes.length);
1110 for (var i = 0; i < len; i += 3) {
1111 var row = document.createElement("tr");
1112 var refCell = document.createElement("td");
1114 appendHelp(refTypes, i, refCell);
1115 row.appendChild(refCell);
1116 var objCell = document.createElement("td");
1118 appendHelp(objTypes, i, objCell);
1119 row.appendChild(objCell);
1120 contentElement.appendChild(row);
1122 this.helpPopover = new WebInspector.Popover(contentElement);
1124 function appendHelp(help, index, cell)
1126 var div = document.createElement("div");
1127 div.className = "source-code event-properties";
1128 var name = document.createElement("span");
1129 name.textContent = help[index];
1130 name.className = help[index + 1];
1131 div.appendChild(name);
1132 var desc = document.createElement("span");
1133 desc.textContent = " " + help[index + 2];
1134 div.appendChild(desc);
1135 cell.appendChild(div);
1138 if (this.helpPopover.visible)
1139 this.helpPopover.hide();
1141 this.helpPopover.show(this.helpButton.element);
1144 _startRetainersHeaderDragging: function(event)
1146 if (!this.visible || event.target === this.retainingPathsRoot)
1149 WebInspector.elementDragStart(this.retainmentViewHeader, this._retainersHeaderDragging.bind(this), this._endRetainersHeaderDragging.bind(this), event, "row-resize");
1150 this._previousDragPosition = event.pageY;
1151 event.stopPropagation();
1154 _retainersHeaderDragging: function(event)
1156 var height = this.retainmentView.element.clientHeight;
1157 height += this._previousDragPosition - event.pageY;
1158 this._previousDragPosition = event.pageY;
1159 this._updateRetainmentViewHeight(height);
1160 event.preventDefault();
1161 event.stopPropagation();
1164 _endRetainersHeaderDragging: function(event)
1166 WebInspector.elementDragEnd(event);
1167 delete this._previousDragPosition;
1168 event.stopPropagation();
1171 _updateRetainmentViewHeight: function(height)
1173 height = Number.constrain(height, Preferences.minConsoleHeight, this.element.clientHeight - Preferences.minConsoleHeight);
1174 this.viewsContainer.style.bottom = (height + this.retainmentViewHeader.clientHeight) + "px";
1175 this.retainmentView.element.style.height = height + "px";
1176 this.retainmentViewHeader.style.bottom = height + "px";
1179 _updateBaseOptions: function()
1181 var list = this._profiles();
1182 // We're assuming that snapshots can only be added.
1183 if (this.baseSelectElement.length === list.length)
1186 for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) {
1187 var baseOption = document.createElement("option");
1188 var title = list[i].title;
1189 if (!title.indexOf(UserInitiatedProfileName))
1190 title = WebInspector.UIString("Snapshot %d", title.substring(UserInitiatedProfileName.length + 1));
1191 baseOption.label = title;
1192 this.baseSelectElement.appendChild(baseOption);
1196 _updatePercentButton: function()
1198 if (this._isShowingAsPercent) {
1199 this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes.");
1200 this.percentButton.toggled = true;
1202 this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages.");
1203 this.percentButton.toggled = false;
1208 WebInspector.DetailedHeapshotView.prototype.__proto__ = WebInspector.View.prototype;
1210 WebInspector.DetailedHeapshotView.prototype.showHiddenData = true;
1212 WebInspector.DetailedHeapshotProfileType = function()
1214 WebInspector.ProfileType.call(this, WebInspector.DetailedHeapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS"));
1217 WebInspector.DetailedHeapshotProfileType.TypeId = "HEAP";
1219 WebInspector.DetailedHeapshotProfileType.prototype = {
1222 return WebInspector.UIString("Take heap snapshot.");
1227 return "heap-snapshot-status-bar-item status-bar-item";
1230 buttonClicked: function()
1232 WebInspector.panels.profiles.takeHeapSnapshot();
1235 get welcomeMessage()
1237 return WebInspector.UIString("Get a heap snapshot by pressing the %s button on the status bar.");
1240 createSidebarTreeElementForProfile: function(profile)
1242 return new WebInspector.ProfileSidebarTreeElement(profile, WebInspector.UIString("Snapshot %d"), "heap-snapshot-sidebar-tree-item");
1245 createView: function(profile)
1247 return new WebInspector.DetailedHeapshotView(WebInspector.panels.profiles, profile);
1251 WebInspector.DetailedHeapshotProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype;