| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WebInspector.HeapSnapshotContainmentDataGrid = function() |
| { |
| var columns = { |
| object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true, sort: "ascending" }, |
| shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, |
| retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sortable: true } |
| }; |
| WebInspector.DataGrid.call(this, columns); |
| this.addEventListener("sorting changed", this.sort, this); |
| } |
| |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype = { |
| _defaultPopulateCount: 100, |
| |
| setDataSource: function(snapshotView, snapshot) |
| { |
| this.snapshotView = snapshotView; |
| this.snapshot = snapshot; |
| this.snapshotNodeIndex = this.snapshot.rootNodeIndex; |
| this._provider = this._createProvider(snapshot, this.snapshotNodeIndex); |
| this.sort(); |
| } |
| }; |
| |
| MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotObjectNode.prototype, WebInspector.HeapSnapshotContainmentDataGrid.prototype); |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; |
| |
| WebInspector.HeapSnapshotSortableDataGrid = function(columns) |
| { |
| WebInspector.DataGrid.call(this, columns); |
| this.addEventListener("sorting changed", this.sortingChanged, this); |
| } |
| |
| WebInspector.HeapSnapshotSortableDataGrid.prototype = { |
| sortingChanged: function() |
| { |
| var sortAscending = this.sortOrder === "ascending"; |
| var sortColumnIdentifier = this.sortColumnIdentifier; |
| if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending) |
| return; |
| this._lastSortColumnIdentifier = sortColumnIdentifier; |
| this._lastSortAscending = sortAscending; |
| var sortFields = this._sortFields(sortColumnIdentifier, sortAscending); |
| |
| function SortByTwoFields(nodeA, nodeB) |
| { |
| var field1 = nodeA[sortFields[0]]; |
| var field2 = nodeB[sortFields[0]]; |
| var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); |
| if (!sortFields[1]) |
| result = -result; |
| if (result !== 0) |
| return result; |
| field1 = nodeA[sortFields[2]]; |
| field2 = nodeB[sortFields[2]]; |
| result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); |
| if (!sortFields[3]) |
| result = -result; |
| return result; |
| } |
| |
| this._performSorting(SortByTwoFields); |
| }, |
| |
| _performSorting: function(sortFunction) |
| { |
| this.dispatchEventToListeners("start sorting"); |
| var children = this.children; |
| this.removeChildren(); |
| children.sort(sortFunction); |
| for (var i = 0, l = children.length; i < l; ++i) { |
| var child = children[i]; |
| this.appendChild(child); |
| if (child.expanded) |
| child.sort(); |
| } |
| this.dispatchEventToListeners("sorting complete"); |
| } |
| }; |
| |
| WebInspector.HeapSnapshotSortableDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; |
| |
| WebInspector.HeapSnapshotConstructorsDataGrid = function() |
| { |
| var columns = { |
| object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, |
| count: { title: WebInspector.UIString("#"), width: "45px", sortable: true }, |
| shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, |
| retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true } |
| }; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| } |
| |
| WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { |
| _defaultPopulateCount: 100, |
| |
| _sortFields: function(sortColumn, sortAscending) |
| { |
| return { |
| object: ["_name", sortAscending, "_count", false], |
| count: ["_count", sortAscending, "_name", true], |
| shallowSize: ["_shallowSize", sortAscending, "_name", true], |
| retainedSize: ["_retainedSize", sortAscending, "_name", true] |
| }[sortColumn]; |
| }, |
| |
| setDataSource: function(snapshotView, snapshot) |
| { |
| this.snapshotView = snapshotView; |
| this.snapshot = snapshot; |
| this.populateChildren(); |
| }, |
| |
| populateChildren: function() |
| { |
| function aggregatesReceived(aggregates) |
| { |
| for (var constructor in aggregates) |
| this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor])); |
| this.sortingChanged(); |
| } |
| this.snapshot.aggregates(false, aggregatesReceived.bind(this)); |
| } |
| }; |
| |
| WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; |
| |
| WebInspector.HeapSnapshotDiffDataGrid = function() |
| { |
| var columns = { |
| object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, |
| addedCount: { title: WebInspector.UIString("# New"), width: "72px", sortable: true, sort: "descending" }, |
| removedCount: { title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true }, |
| // \u0394 is a Greek delta letter. |
| countDelta: { title: "\u0394", width: "40px", sortable: true }, |
| addedSize: { title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true }, |
| removedSize: { title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true }, |
| sizeDelta: { title: "\u0394", width: "72px", sortable: true } |
| }; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| } |
| |
| WebInspector.HeapSnapshotDiffDataGrid.prototype = { |
| _defaultPopulateCount: 50, |
| |
| _sortFields: function(sortColumn, sortAscending) |
| { |
| return { |
| object: ["_name", sortAscending, "_count", false], |
| addedCount: ["_addedCount", sortAscending, "_name", true], |
| removedCount: ["_removedCount", sortAscending, "_name", true], |
| countDelta: ["_countDelta", sortAscending, "_name", true], |
| addedSize: ["_addedSize", sortAscending, "_name", true], |
| removedSize: ["_removedSize", sortAscending, "_name", true], |
| sizeDelta: ["_sizeDelta", sortAscending, "_name", true] |
| }[sortColumn]; |
| }, |
| |
| setDataSource: function(snapshotView, snapshot) |
| { |
| this.snapshotView = snapshotView; |
| this.snapshot = snapshot; |
| }, |
| |
| setBaseDataSource: function(baseSnapshot) |
| { |
| this.baseSnapshot = baseSnapshot; |
| this.removeChildren(); |
| if (this.baseSnapshot === this.snapshot) |
| return; |
| this.populateChildren(); |
| }, |
| |
| populateChildren: function() |
| { |
| function baseAggregatesReceived(baseClasses) |
| { |
| function aggregatesReceived(classes) |
| { |
| var nodeCount = 0; |
| function addNodeIfNonZeroDiff(node, zeroDiff) |
| { |
| if (!zeroDiff) |
| this.appendChild(node); |
| if (!--nodeCount) |
| this.sortingChanged(); |
| } |
| for (var clss in baseClasses) { |
| var node = new WebInspector.HeapSnapshotDiffNode(this, clss, baseClasses[clss], classes[clss]); |
| ++nodeCount; |
| node.calculateDiff(this, addNodeIfNonZeroDiff.bind(this, node)); |
| } |
| for (clss in classes) { |
| if (!(clss in baseClasses)) { |
| var node = new WebInspector.HeapSnapshotDiffNode(this, clss, null, classes[clss]); |
| ++nodeCount; |
| node.calculateDiff(this, addNodeIfNonZeroDiff.bind(this, node)); |
| } |
| } |
| } |
| this.snapshot.aggregates(true, aggregatesReceived.bind(this)); |
| } |
| this.baseSnapshot.aggregates(true, baseAggregatesReceived.bind(this)); |
| } |
| }; |
| |
| WebInspector.HeapSnapshotDiffDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; |
| |
| WebInspector.HeapSnapshotDominatorsDataGrid = function() |
| { |
| var columns = { |
| object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true }, |
| shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, |
| retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true } |
| }; |
| WebInspector.DataGrid.call(this, columns); |
| this.addEventListener("sorting changed", this.sort, this); |
| } |
| |
| WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { |
| _defaultPopulateCount: 25, |
| |
| setDataSource: function(snapshotView, snapshot) |
| { |
| this.snapshotView = snapshotView; |
| this.snapshot = snapshot; |
| this.snapshotNodeIndex = this.snapshot.rootNodeIndex; |
| this._provider = this._createProvider(snapshot, this.snapshotNodeIndex); |
| this.sort(); |
| } |
| }; |
| |
| MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotDominatorObjectNode.prototype, WebInspector.HeapSnapshotDominatorsDataGrid.prototype); |
| WebInspector.HeapSnapshotDominatorsDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; |
| |
| WebInspector.HeapSnapshotRetainingPathsList = function() |
| { |
| var columns = { |
| path: { title: WebInspector.UIString("Retaining path"), sortable: true }, |
| len: { title: WebInspector.UIString("Length"), width: "90px", sortable: true, sort: "ascending" } |
| }; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| this._defaultPopulateCount = 100; |
| } |
| |
| WebInspector.HeapSnapshotRetainingPathsList.prototype = { |
| _sortFields: function(sortColumn, sortAscending) |
| { |
| return { |
| path: ["path", sortAscending, "len", true], |
| len: ["len", sortAscending, "path", true] |
| }[sortColumn]; |
| }, |
| |
| _resetPaths: function() |
| { |
| this._setRootChildrenForFinder(); |
| this.removeChildren(); |
| this._counter = 0; |
| this.showNext(this._defaultPopulateCount); |
| }, |
| |
| setDataSource: function(snapshotView, snapshot, nodeIndex, prefix) |
| { |
| this.snapshotView = snapshotView; |
| this._prefix = prefix; |
| |
| if (this.pathFinder) |
| this.searchCancelled(); |
| this.pathFinder = snapshot.createPathFinder(nodeIndex); |
| this._resetPaths(); |
| }, |
| |
| refresh: function() |
| { |
| delete this._cancel; |
| this._resetPaths(); |
| }, |
| |
| showNext: function(pathsCount) |
| { |
| WebInspector.PleaseWaitMessage.prototype.show(this.element, this.searchCancelled.bind(this, pathsCount)); |
| |
| function pathFound(result) |
| { |
| if (result === null) { |
| WebInspector.PleaseWaitMessage.prototype.hide(); |
| if (!this.children.length) |
| this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("Can't find any paths."), len:""}, false)); |
| return; |
| } else if (result !== false) { |
| if (this._prefix) |
| result.path = this._prefix + result.path; |
| this.appendChild(new WebInspector.DataGridNode(result, false)); |
| ++this._counter; |
| } |
| setTimeout(startSearching.bind(this), 0); |
| } |
| |
| function startSearching() |
| { |
| if (this._cancel === this.pathFinder) |
| return; |
| delete this._cancel; |
| if (this._counter < pathsCount) |
| this.pathFinder.findNext(pathFound.bind(this)); |
| else { |
| this.searchCancelled.call(this, pathsCount); |
| delete this._cancel; |
| } |
| } |
| setTimeout(startSearching.bind(this), 0); |
| }, |
| |
| searchCancelled: function(pathsCount) |
| { |
| WebInspector.PleaseWaitMessage.prototype.hide(); |
| this._counter = 0; |
| this._cancel = this.pathFinder; |
| if (pathsCount) { |
| this.appendChild(new WebInspector.ShowMoreDataGridNode(this.showNext.bind(this), pathsCount)); |
| this.sortingChanged(); |
| } |
| }, |
| |
| _setRootChildrenForFinder: function() |
| { |
| function FilterDOMWindow(node) |
| { |
| return node.name === "DOMWindow"; |
| } |
| |
| if (this.snapshotView.isTracingToWindowObjects) |
| this.pathFinder.updateRoots(FilterDOMWindow); |
| else |
| this.pathFinder.updateRoots(); |
| }, |
| |
| _performSorting: function(sortFunction) |
| { |
| function DataExtractorWrapper(nodeA, nodeB) |
| { |
| return sortFunction(nodeA.data, nodeB.data); |
| } |
| |
| this.sortNodes(DataExtractorWrapper); |
| } |
| }; |
| |
| WebInspector.HeapSnapshotRetainingPathsList.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; |
| |
| WebInspector.DetailedHeapshotView = function(parent, profile) |
| { |
| WebInspector.View.call(this); |
| |
| this.element.addStyleClass("detailed-heapshot-view"); |
| |
| this.parent = parent; |
| this.parent.addEventListener("profile added", this._updateBaseOptions, this); |
| |
| this.showCountAsPercent = false; |
| this.showShallowSizeAsPercent = false; |
| this.showRetainedSizeAsPercent = false; |
| |
| this.containmentView = new WebInspector.View(); |
| this.containmentView.element.addStyleClass("view"); |
| this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(); |
| this.containmentDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); |
| this.containmentView.element.appendChild(this.containmentDataGrid.element); |
| this.element.appendChild(this.containmentView.element); |
| |
| this.constructorsView = new WebInspector.View(); |
| this.constructorsView.element.addStyleClass("view"); |
| this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(); |
| this.constructorsDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); |
| this.constructorsView.element.appendChild(this.constructorsDataGrid.element); |
| this.element.appendChild(this.constructorsView.element); |
| |
| this.diffView = new WebInspector.View(); |
| this.diffView.element.addStyleClass("view"); |
| this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(); |
| this.diffDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); |
| this.diffView.element.appendChild(this.diffDataGrid.element); |
| this.element.appendChild(this.diffView.element); |
| |
| this.dominatorView = new WebInspector.View(); |
| this.dominatorView.element.addStyleClass("view"); |
| this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid(); |
| this.dominatorDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); |
| this.dominatorView.element.appendChild(this.dominatorDataGrid.element); |
| this.element.appendChild(this.dominatorView.element); |
| |
| var retainmentView = new WebInspector.View(); |
| retainmentView.element.addStyleClass("view"); |
| retainmentView.element.addStyleClass("retaining-paths-view"); |
| var retainingPathsTitleDiv = document.createElement("div"); |
| retainingPathsTitleDiv.className = "title"; |
| var retainingPathsTitle = document.createElement("span"); |
| retainingPathsTitle.textContent = WebInspector.UIString("Paths from the selected object"); |
| this.retainingPathsRoot = document.createElement("select"); |
| this.retainingPathsRoot.className = "status-bar-item"; |
| this.retainingPathsRoot.addEventListener("change", this._changeRetainingPathsRoot.bind(this), false); |
| var toGCRootsTraceOption = document.createElement("option"); |
| toGCRootsTraceOption.label = WebInspector.UIString("to GC roots"); |
| var toWindowObjectsTraceOption = document.createElement("option"); |
| toWindowObjectsTraceOption.label = WebInspector.UIString("to window objects"); |
| this.retainingPathsRoot.appendChild(toGCRootsTraceOption); |
| this.retainingPathsRoot.appendChild(toWindowObjectsTraceOption); |
| retainingPathsTitleDiv.appendChild(retainingPathsTitle); |
| retainingPathsTitleDiv.appendChild(this.retainingPathsRoot); |
| retainmentView.element.appendChild(retainingPathsTitleDiv); |
| this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainingPathsList(); |
| retainmentView.element.appendChild(this.retainmentDataGrid.element); |
| retainmentView.visible = true; |
| this.element.appendChild(retainmentView.element); |
| |
| this.dataGrid = this.constructorsDataGrid; |
| this.currentView = this.constructorsView; |
| |
| this.viewSelectElement = document.createElement("select"); |
| this.viewSelectElement.className = "status-bar-item"; |
| this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false); |
| |
| this.views = [{title: "Summary", view: this.constructorsView, grid: this.constructorsDataGrid}, |
| {title: "Comparison", view: this.diffView, grid: this.diffDataGrid}, |
| {title: "Containment", view: this.containmentView, grid: this.containmentDataGrid}, |
| {title: "Dominators", view: this.dominatorView, grid: this.dominatorDataGrid}]; |
| this.views.current = 0; |
| for (var i = 0; i < this.views.length; ++i) { |
| var view = this.views[i]; |
| var option = document.createElement("option"); |
| option.label = WebInspector.UIString(view.title); |
| this.viewSelectElement.appendChild(option); |
| } |
| |
| this._profileUid = profile.uid; |
| |
| this.baseSelectElement = document.createElement("select"); |
| this.baseSelectElement.className = "status-bar-item hidden"; |
| this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false); |
| this._updateBaseOptions(); |
| |
| this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item"); |
| this.percentButton.addEventListener("click", this._percentClicked.bind(this), false); |
| this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item"); |
| this.helpButton.addEventListener("click", this._helpClicked.bind(this), false); |
| |
| var popoverHelper = new WebInspector.PopoverHelper(this.element, this._getHoverAnchor.bind(this), this._showStringContentPopup.bind(this)); |
| |
| this._loadProfile(this._profileUid, profileCallback.bind(this)); |
| |
| function profileCallback() |
| { |
| var list = this._profiles(); |
| var profileIndex; |
| for (var i = 0; i < list.length; ++i) |
| if (list[i].uid === this._profileUid) { |
| profileIndex = i; |
| break; |
| } |
| if (profileIndex > 0) |
| this.baseSelectElement.selectedIndex = profileIndex - 1; |
| else |
| this.baseSelectElement.selectedIndex = profileIndex; |
| this.dataGrid.setDataSource(this, this.profileWrapper); |
| this._updatePercentButton(); |
| } |
| } |
| |
| WebInspector.DetailedHeapshotView.prototype = { |
| dispose: function() |
| { |
| if (this._profileWrapper) |
| this._profileWrapper.dispose(); |
| if (this._baseProfileWrapper) |
| this._baseProfileWrapper.dispose(); |
| }, |
| |
| get statusBarItems() |
| { |
| return [this.viewSelectElement, this.baseSelectElement, this.percentButton.element, this.helpButton.element]; |
| }, |
| |
| get profile() |
| { |
| return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._profileUid); |
| }, |
| |
| get profileWrapper() |
| { |
| if (!this._profileWrapper) |
| this._profileWrapper = this.profile.proxy; |
| return this._profileWrapper; |
| }, |
| |
| get baseProfile() |
| { |
| return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._baseProfileUid); |
| }, |
| |
| get baseProfileWrapper() |
| { |
| if (!this._baseProfileWrapper) |
| this._baseProfileWrapper = this.baseProfile.proxy; |
| return this._baseProfileWrapper; |
| }, |
| |
| show: function(parentElement) |
| { |
| WebInspector.View.prototype.show.call(this, parentElement); |
| if (!this.profileWrapper.loaded) |
| this._loadProfile(this._profileUid, profileCallback1.bind(this)); |
| else |
| profileCallback1.call(this); |
| |
| function profileCallback1() { |
| if (this.baseProfile && !this.baseProfileWrapper.loaded) |
| this._loadProfile(this._baseProfileUid, profileCallback2.bind(this)); |
| else |
| profileCallback2.call(this); |
| } |
| |
| function profileCallback2() { |
| this.currentView.show(); |
| this.dataGrid.updateWidths(); |
| } |
| }, |
| |
| hide: function() |
| { |
| WebInspector.View.prototype.hide.call(this); |
| this._currentSearchResultIndex = -1; |
| }, |
| |
| resize: function() |
| { |
| if (this.dataGrid) |
| this.dataGrid.updateWidths(); |
| }, |
| |
| refreshShowAsPercents: function() |
| { |
| this._updatePercentButton(); |
| this.refreshVisibleData(); |
| }, |
| |
| searchCanceled: function() |
| { |
| if (this._searchResults) { |
| for (var i = 0; i < this._searchResults.length; ++i) { |
| var node = this._searchResults[i].node; |
| delete node._searchMatched; |
| node.refresh(); |
| } |
| } |
| |
| delete this._searchFinishedCallback; |
| this._currentSearchResultIndex = -1; |
| this._searchResults = []; |
| }, |
| |
| performSearch: function(query, finishedCallback) |
| { |
| // Call searchCanceled since it will reset everything we need before doing a new search. |
| this.searchCanceled(); |
| |
| query = query.trim(); |
| |
| if (!query.length) |
| return; |
| if (this.currentView !== this.constructorsView && this.currentView !== this.diffView) |
| return; |
| |
| this._searchFinishedCallback = finishedCallback; |
| |
| function matchesByName(gridNode) { |
| return ("name" in gridNode) && gridNode.name.hasSubstring(query, true); |
| } |
| |
| function matchesById(gridNode) { |
| return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query; |
| } |
| |
| var matchPredicate; |
| if (query.charAt(0) !== "@") |
| matchPredicate = matchesByName; |
| else { |
| query = parseInt(query.substring(1), 10); |
| matchPredicate = matchesById; |
| } |
| |
| function matchesQuery(gridNode) |
| { |
| delete gridNode._searchMatched; |
| if (matchPredicate(gridNode)) { |
| gridNode._searchMatched = true; |
| gridNode.refresh(); |
| return true; |
| } |
| return false; |
| } |
| |
| var current = this.dataGrid.children[0]; |
| var depth = 0; |
| var info = {}; |
| |
| // Restrict to type nodes and instances. |
| const maxDepth = 1; |
| |
| while (current) { |
| if (matchesQuery(current)) |
| this._searchResults.push({ node: current }); |
| current = current.traverseNextNode(false, null, (depth >= maxDepth), info); |
| depth += info.depthChange; |
| } |
| |
| finishedCallback(this, this._searchResults.length); |
| }, |
| |
| jumpToFirstSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| this._currentSearchResultIndex = 0; |
| this._jumpToSearchResult(this._currentSearchResultIndex); |
| }, |
| |
| jumpToLastSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| this._currentSearchResultIndex = (this._searchResults.length - 1); |
| this._jumpToSearchResult(this._currentSearchResultIndex); |
| }, |
| |
| jumpToNextSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| if (++this._currentSearchResultIndex >= this._searchResults.length) |
| this._currentSearchResultIndex = 0; |
| this._jumpToSearchResult(this._currentSearchResultIndex); |
| }, |
| |
| jumpToPreviousSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| if (--this._currentSearchResultIndex < 0) |
| this._currentSearchResultIndex = (this._searchResults.length - 1); |
| this._jumpToSearchResult(this._currentSearchResultIndex); |
| }, |
| |
| showingFirstSearchResult: function() |
| { |
| return (this._currentSearchResultIndex === 0); |
| }, |
| |
| showingLastSearchResult: function() |
| { |
| return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1)); |
| }, |
| |
| _jumpToSearchResult: function(index) |
| { |
| var searchResult = this._searchResults[index]; |
| if (!searchResult) |
| return; |
| |
| var node = searchResult.node; |
| node.reveal(); |
| node.select(); |
| }, |
| |
| refreshVisibleData: function() |
| { |
| var child = this.dataGrid.children[0]; |
| while (child) { |
| child.refresh(); |
| child = child.traverseNextNode(false, null, true); |
| } |
| }, |
| |
| _changeBase: function() |
| { |
| if (this._baseProfileUid === this._profiles()[this.baseSelectElement.selectedIndex].uid) |
| return; |
| |
| this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid; |
| this._loadProfile(this._baseProfileUid, baseProfileLoaded.bind(this)); |
| |
| function baseProfileLoaded() |
| { |
| delete this._baseProfileWrapper; |
| this.baseProfile._lastShown = Date.now(); |
| WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, showDiffData.bind(this)); |
| } |
| |
| function showDiffData() |
| { |
| this.diffDataGrid.setBaseDataSource(this.baseProfileWrapper); |
| } |
| |
| if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) |
| return; |
| |
| // The current search needs to be performed again. First negate out previous match |
| // count by calling the search finished callback with a negative number of matches. |
| // Then perform the search again with the same query and callback. |
| this._searchFinishedCallback(this, -this._searchResults.length); |
| this.performSearch(this.currentQuery, this._searchFinishedCallback); |
| }, |
| |
| _profiles: function() |
| { |
| return WebInspector.panels.profiles.getProfiles(WebInspector.HeapSnapshotProfileType.TypeId); |
| }, |
| |
| _loadProfile: function(profileUid, callback) |
| { |
| WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback); |
| }, |
| |
| isDetailedSnapshot: function(snapshot) |
| { |
| var s = new WebInspector.HeapSnapshot(snapshot); |
| for (var iter = s.rootNode.edges; iter.hasNext(); iter.next()) |
| if (iter.edge.node.name === "(GC roots)") |
| return true; |
| return false; |
| }, |
| |
| processLoadedSnapshot: function(profile, snapshot) |
| { |
| profile.nodes = snapshot.nodes; |
| profile.strings = snapshot.strings; |
| var s = new WebInspector.HeapSnapshot(profile); |
| profile.sideBarElement.subtitle = Number.bytesToString(s.totalSize); |
| }, |
| |
| _mouseClickInContainmentGrid: function(event) |
| { |
| var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); |
| if (!cell || (!cell.hasStyleClass("object-column") && !cell.hasStyleClass("shallowSize-column") && !cell.hasStyleClass("retainedSize-column"))) |
| return; |
| var row = event.target.enclosingNodeOrSelfWithNodeName("tr"); |
| if (!row) |
| return; |
| var nodeItem = row._dataGridNode; |
| if (!nodeItem || nodeItem.isEventWithinDisclosureTriangle(event) || !nodeItem.snapshotNodeIndex) |
| return; |
| |
| this.retainmentDataGrid.setDataSource(this, nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex, nodeItem.isDeletedNode ? this.baseSelectElement.childNodes[this.baseSelectElement.selectedIndex].label + " | " : ""); |
| }, |
| |
| _changeView: function(event) |
| { |
| if (!event || !this._profileUid) |
| return; |
| if (event.target.selectedIndex === this.views.current) |
| return; |
| |
| this.views.current = event.target.selectedIndex; |
| this.currentView.hide(); |
| var view = this.views[this.views.current]; |
| this.currentView = view.view; |
| this.dataGrid = view.grid; |
| this.currentView.show(); |
| this.refreshVisibleData(); |
| if (this.currentView === this.diffView) { |
| this.baseSelectElement.removeStyleClass("hidden"); |
| if (!this.dataGrid.snapshotView) { |
| this.dataGrid.setDataSource(this, this.profileWrapper); |
| this._changeBase(); |
| } |
| } else { |
| this.baseSelectElement.addStyleClass("hidden"); |
| if (!this.dataGrid.snapshotView) |
| WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, loadData.bind(this)); |
| } |
| |
| function loadData() |
| { |
| this.dataGrid.setDataSource(this, this.profileWrapper); |
| } |
| |
| if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) |
| return; |
| |
| // The current search needs to be performed again. First negate out previous match |
| // count by calling the search finished callback with a negative number of matches. |
| // Then perform the search again the with same query and callback. |
| this._searchFinishedCallback(this, -this._searchResults.length); |
| this.performSearch(this.currentQuery, this._searchFinishedCallback); |
| }, |
| |
| _changeRetainingPathsRoot: function(event) |
| { |
| if (!event) |
| return; |
| this.retainmentDataGrid.refresh(); |
| }, |
| |
| _getHoverAnchor: function(target) |
| { |
| var span = target.enclosingNodeOrSelfWithNodeName("span"); |
| if (!span || !span.hasStyleClass("console-formatted-string")) |
| return; |
| var row = target.enclosingNodeOrSelfWithNodeName("tr"); |
| if (!row) |
| return; |
| var gridNode = row._dataGridNode; |
| if (!gridNode.snapshotNodeIndex) |
| return; |
| span.snapshotNodeIndex = gridNode.snapshotNodeIndex; |
| return span; |
| }, |
| |
| get isTracingToWindowObjects() |
| { |
| return this.retainingPathsRoot.selectedIndex === 1; |
| }, |
| |
| get _isShowingAsPercent() |
| { |
| return this.showCountAsPercent && this.showShallowSizeAsPercent && this.showRetainedSizeAsPercent; |
| }, |
| |
| _percentClicked: function(event) |
| { |
| var currentState = this._isShowingAsPercent; |
| this.showCountAsPercent = !currentState; |
| this.showShallowSizeAsPercent = !currentState; |
| this.showRetainedSizeAsPercent = !currentState; |
| this.refreshShowAsPercents(); |
| }, |
| |
| _showStringContentPopup: function(span) |
| { |
| var snapshotNode = new WebInspector.HeapSnapshotNode(this.profileWrapper, span.snapshotNodeIndex); |
| var stringContentElement = document.createElement("span"); |
| stringContentElement.className = "monospace console-formatted-string"; |
| stringContentElement.style.whiteSpace = "pre"; |
| stringContentElement.textContent = "\"" + snapshotNode.name + "\""; |
| var popover = new WebInspector.Popover(stringContentElement); |
| popover.show(span); |
| return popover; |
| }, |
| |
| _helpClicked: function(event) |
| { |
| if (!this.helpPopover) { |
| var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"), |
| "0:", "console-formatted-name", WebInspector.UIString("element"), |
| "a:", "console-formatted-number", WebInspector.UIString("context var"), |
| "a:", "console-formatted-null", WebInspector.UIString("system prop")]; |
| var objTypes = [" a ", "console-formatted-object", "Object", |
| "\"a\"", "console-formatted-string", "String", |
| "/a/", "console-formatted-string", "RegExp", |
| "a()", "console-formatted-function", "Function", |
| "a[]", "console-formatted-object", "Array", |
| "num", "console-formatted-number", "Number", |
| " a ", "console-formatted-null", "System"]; |
| |
| var contentElement = document.createElement("table"); |
| contentElement.className = "heapshot-help"; |
| var headerRow = document.createElement("tr"); |
| var propsHeader = document.createElement("th"); |
| propsHeader.textContent = WebInspector.UIString("Property types:"); |
| headerRow.appendChild(propsHeader); |
| var objsHeader = document.createElement("th"); |
| objsHeader.textContent = WebInspector.UIString("Object types:"); |
| headerRow.appendChild(objsHeader); |
| contentElement.appendChild(headerRow); |
| var len = Math.max(refTypes.length, objTypes.length); |
| for (var i = 0; i < len; i += 3) { |
| var row = document.createElement("tr"); |
| var refCell = document.createElement("td"); |
| if (refTypes[i]) |
| appendHelp(refTypes, i, refCell); |
| row.appendChild(refCell); |
| var objCell = document.createElement("td"); |
| if (objTypes[i]) |
| appendHelp(objTypes, i, objCell); |
| row.appendChild(objCell); |
| contentElement.appendChild(row); |
| } |
| this.helpPopover = new WebInspector.Popover(contentElement); |
| |
| function appendHelp(help, index, cell) |
| { |
| var div = document.createElement("div"); |
| div.className = "source-code event-properties"; |
| var name = document.createElement("span"); |
| name.textContent = help[index]; |
| name.className = help[index + 1]; |
| div.appendChild(name); |
| var desc = document.createElement("span"); |
| desc.textContent = " " + help[index + 2]; |
| div.appendChild(desc); |
| cell.appendChild(div); |
| } |
| } |
| if (this.helpPopover.visible) |
| this.helpPopover.hide(); |
| else |
| this.helpPopover.show(this.helpButton.element); |
| }, |
| |
| _updateBaseOptions: function() |
| { |
| var list = this._profiles(); |
| // We're assuming that snapshots can only be added. |
| if (this.baseSelectElement.length === list.length) |
| return; |
| |
| for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) { |
| var baseOption = document.createElement("option"); |
| var title = list[i].title; |
| if (!title.indexOf(UserInitiatedProfileName)) |
| title = WebInspector.UIString("Snapshot %d", title.substring(UserInitiatedProfileName.length + 1)); |
| baseOption.label = title; |
| this.baseSelectElement.appendChild(baseOption); |
| } |
| }, |
| |
| _updatePercentButton: function() |
| { |
| if (this._isShowingAsPercent) { |
| this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes."); |
| this.percentButton.toggled = true; |
| } else { |
| this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages."); |
| this.percentButton.toggled = false; |
| } |
| } |
| }; |
| |
| WebInspector.DetailedHeapshotView.prototype.__proto__ = WebInspector.View.prototype; |
| |
| WebInspector.DetailedHeapshotView.prototype.showHiddenData = true; |
| |
| WebInspector.DetailedHeapshotProfileType = function() |
| { |
| WebInspector.ProfileType.call(this, WebInspector.HeapSnapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS")); |
| } |
| |
| WebInspector.DetailedHeapshotProfileType.prototype = { |
| get buttonTooltip() |
| { |
| return WebInspector.UIString("Take heap snapshot."); |
| }, |
| |
| get buttonStyle() |
| { |
| return "heap-snapshot-status-bar-item status-bar-item"; |
| }, |
| |
| buttonClicked: function() |
| { |
| WebInspector.panels.profiles.takeHeapSnapshot(true); |
| }, |
| |
| get welcomeMessage() |
| { |
| return WebInspector.UIString("Get a heap snapshot by pressing the %s button on the status bar."); |
| }, |
| |
| createSidebarTreeElementForProfile: function(profile) |
| { |
| return new WebInspector.ProfileSidebarTreeElement(profile, WebInspector.UIString("Snapshot %d"), "heap-snapshot-sidebar-tree-item"); |
| }, |
| |
| createView: function(profile) |
| { |
| return new WebInspector.DetailedHeapshotView(WebInspector.panels.profiles, profile); |
| } |
| } |
| |
| WebInspector.DetailedHeapshotProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype; |