| /* |
| * Copyright (C) 2012 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. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.DataGrid} |
| */ |
| WebInspector.HeapSnapshotSortableDataGrid = function(columns) |
| { |
| WebInspector.DataGrid.call(this, columns); |
| |
| /** |
| * @type {number} |
| */ |
| this._recursiveSortingDepth = 0; |
| /** |
| * @type {WebInspector.HeapSnapshotGridNode} |
| */ |
| this._highlightedNode = null; |
| /** |
| * @type {boolean} |
| */ |
| this._populatedAndSorted = false; |
| this.addEventListener("sorting complete", this._sortingComplete, this); |
| this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this); |
| } |
| |
| WebInspector.HeapSnapshotSortableDataGrid.Events = { |
| ContentShown: "ContentShown" |
| } |
| |
| WebInspector.HeapSnapshotSortableDataGrid.prototype = { |
| /** |
| * @return {number} |
| */ |
| defaultPopulateCount: function() |
| { |
| return 100; |
| }, |
| |
| dispose: function() |
| { |
| var children = this.topLevelNodes(); |
| for (var i = 0, l = children.length; i < l; ++i) |
| children[i].dispose(); |
| }, |
| |
| /** |
| * @override |
| */ |
| wasShown: function() |
| { |
| if (this._populatedAndSorted) |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); |
| }, |
| |
| _sortingComplete: function() |
| { |
| this.removeEventListener("sorting complete", this._sortingComplete, this); |
| this._populatedAndSorted = true; |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); |
| }, |
| |
| /** |
| * @override |
| */ |
| willHide: function() |
| { |
| this._clearCurrentHighlight(); |
| }, |
| |
| /** |
| * @param {WebInspector.ProfilesPanel} profilesPanel |
| * @param {WebInspector.ContextMenu} contextMenu |
| * @param {Event} event |
| */ |
| populateContextMenu: function(profilesPanel, contextMenu, event) |
| { |
| var td = event.target.enclosingNodeOrSelfWithNodeName("td"); |
| if (!td) |
| return; |
| var node = td.heapSnapshotNode; |
| function revealInDominatorsView() |
| { |
| profilesPanel.showObject(node.snapshotNodeId, "Dominators"); |
| } |
| function revealInSummaryView() |
| { |
| profilesPanel.showObject(node.snapshotNodeId, "Summary"); |
| } |
| if(node && node.showRetainingEdges) { |
| contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); |
| contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this)); |
| } |
| else if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) { |
| contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this)); |
| } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) { |
| contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this)); |
| } |
| }, |
| |
| resetSortingCache: function() |
| { |
| delete this._lastSortColumnIdentifier; |
| delete this._lastSortAscending; |
| }, |
| |
| topLevelNodes: function() |
| { |
| return this.rootNode().children; |
| }, |
| |
| /** |
| * @param {HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId |
| */ |
| highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId) |
| { |
| }, |
| |
| /** |
| * @param {WebInspector.HeapSnapshotGridNode} node |
| */ |
| highlightNode: function(node) |
| { |
| var prevNode = this._highlightedNode; |
| this._clearCurrentHighlight(); |
| this._highlightedNode = node; |
| this._highlightedNode.element.addStyleClass("highlighted-row"); |
| // If highlighted node hasn't changed reinsert it to make the highlight animation restart. |
| if (node === prevNode) { |
| var element = node.element; |
| var parent = element.parentElement; |
| var nextSibling = element.nextSibling; |
| parent.removeChild(element); |
| parent.insertBefore(element, nextSibling); |
| } |
| }, |
| |
| nodeWasDetached: function(node) |
| { |
| if (this._highlightedNode === node) |
| this._clearCurrentHighlight(); |
| }, |
| |
| _clearCurrentHighlight: function() |
| { |
| if (!this._highlightedNode) |
| return |
| this._highlightedNode.element.removeStyleClass("highlighted-row"); |
| this._highlightedNode = null; |
| }, |
| |
| changeNameFilter: function(filter) |
| { |
| filter = filter.toLowerCase(); |
| var children = this.topLevelNodes(); |
| for (var i = 0, l = children.length; i < l; ++i) { |
| var node = children[i]; |
| if (node.depth === 0) |
| node.revealed = node._name.toLowerCase().indexOf(filter) !== -1; |
| } |
| this.updateVisibleNodes(); |
| }, |
| |
| sortingChanged: function() |
| { |
| var sortAscending = this.isSortOrderAscending(); |
| 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.recursiveSortingEnter(); |
| var children = this._topLevelNodes; |
| this.rootNode().removeChildren(); |
| children.sort(sortFunction); |
| for (var i = 0, l = children.length; i < l; ++i) { |
| var child = children[i]; |
| this.appendChildAfterSorting(child); |
| if (child.expanded) |
| child.sort(); |
| } |
| this.updateVisibleNodes(); |
| this.recursiveSortingLeave(); |
| }, |
| |
| appendChildAfterSorting: function(child) |
| { |
| var revealed = child.revealed; |
| this.rootNode().appendChild(child); |
| child.revealed = revealed; |
| }, |
| |
| updateVisibleNodes: function() |
| { |
| }, |
| |
| recursiveSortingEnter: function() |
| { |
| ++this._recursiveSortingDepth; |
| }, |
| |
| recursiveSortingLeave: function() |
| { |
| if (!this._recursiveSortingDepth) |
| return; |
| if (!--this._recursiveSortingDepth) |
| this.dispatchEventToListeners("sorting complete"); |
| }, |
| |
| __proto__: WebInspector.DataGrid.prototype |
| } |
| |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotSortableDataGrid} |
| */ |
| WebInspector.HeapSnapshotViewportDataGrid = function(columns) |
| { |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true); |
| this._topLevelNodes = []; |
| this._topPadding = new WebInspector.HeapSnapshotPaddingNode(); |
| this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode(); |
| /** |
| * @type {WebInspector.HeapSnapshotGridNode} |
| */ |
| this._nodeToHighlightAfterScroll = null; |
| } |
| |
| WebInspector.HeapSnapshotViewportDataGrid.prototype = { |
| topLevelNodes: function() |
| { |
| return this._topLevelNodes; |
| }, |
| |
| appendChildAfterSorting: function(child) |
| { |
| // Do nothing here, it will be added in updateVisibleNodes. |
| }, |
| |
| updateVisibleNodes: function() |
| { |
| var scrollTop = this.scrollContainer.scrollTop; |
| |
| var viewPortHeight = this.scrollContainer.offsetHeight; |
| |
| this._removePaddingRows(); |
| |
| var children = this._topLevelNodes; |
| |
| var i = 0; |
| var topPadding = 0; |
| while (i < children.length) { |
| if (children[i].revealed) { |
| var newTop = topPadding + children[i].nodeHeight(); |
| if (newTop > scrollTop) |
| break; |
| topPadding = newTop; |
| } |
| ++i; |
| } |
| |
| var selectedNode = this.selectedNode; |
| |
| this.rootNode().removeChildren(); |
| // The height of the view port + invisible top part. |
| var heightToFill = viewPortHeight + (scrollTop - topPadding); |
| var filledHeight = 0; |
| while (i < children.length && filledHeight < heightToFill) { |
| if (children[i].revealed) { |
| this.rootNode().appendChild(children[i]); |
| filledHeight += children[i].nodeHeight(); |
| } |
| ++i; |
| } |
| |
| var bottomPadding = 0; |
| while (i < children.length) { |
| bottomPadding += children[i].nodeHeight(); |
| ++i; |
| } |
| |
| this._addPaddingRows(topPadding, bottomPadding); |
| |
| if (selectedNode) { |
| if (selectedNode.parent) { |
| selectedNode.select(true); |
| } else { |
| // Keep selection even if the node is not in the current viewport. |
| this.selectedNode = selectedNode; |
| } |
| } |
| }, |
| |
| appendTopLevelNode: function(node) |
| { |
| this._topLevelNodes.push(node); |
| }, |
| |
| removeTopLevelNodes: function() |
| { |
| this.rootNode().removeChildren(); |
| this._topLevelNodes = []; |
| }, |
| |
| /** |
| * @override |
| * @param {WebInspector.HeapSnapshotGridNode} node |
| */ |
| highlightNode: function(node) |
| { |
| if (this._isScrolledIntoView(node.element)) |
| WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node); |
| else { |
| node.element.scrollIntoViewIfNeeded(true); |
| this._nodeToHighlightAfterScroll = node; |
| } |
| }, |
| |
| _isScrolledIntoView: function(element) |
| { |
| var viewportTop = this.scrollContainer.scrollTop; |
| var viewportBottom = viewportTop + this.scrollContainer.clientHeight; |
| var elemTop = element.offsetTop |
| var elemBottom = elemTop + element.offsetHeight; |
| return elemBottom <= viewportBottom && elemTop >= viewportTop; |
| }, |
| |
| _addPaddingRows: function(top, bottom) |
| { |
| if (this._topPadding.element.parentNode !== this.dataTableBody) |
| this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild); |
| if (this._bottomPadding.element.parentNode !== this.dataTableBody) |
| this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild); |
| this._topPadding.setHeight(top); |
| this._bottomPadding.setHeight(bottom); |
| }, |
| |
| _removePaddingRows: function() |
| { |
| this._bottomPadding.removeFromTable(); |
| this._topPadding.removeFromTable(); |
| }, |
| |
| onResize: function() |
| { |
| WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); |
| this.updateVisibleNodes(); |
| }, |
| |
| _onScroll: function(event) |
| { |
| this.updateVisibleNodes(); |
| |
| if (this._nodeToHighlightAfterScroll) { |
| WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll); |
| this._nodeToHighlightAfterScroll = null; |
| } |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype |
| } |
| |
| /** |
| * @constructor |
| */ |
| WebInspector.HeapSnapshotPaddingNode = function() |
| { |
| this.element = document.createElement("tr"); |
| this.element.addStyleClass("revealed"); |
| } |
| |
| WebInspector.HeapSnapshotPaddingNode.prototype = { |
| setHeight: function(height) |
| { |
| this.element.style.height = height + "px"; |
| }, |
| removeFromTable: function() |
| { |
| var parent = this.element.parentNode; |
| if (parent) |
| parent.removeChild(this.element); |
| } |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotSortableDataGrid} |
| * @param {Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns |
| */ |
| WebInspector.HeapSnapshotContainmentDataGrid = function(columns) |
| { |
| columns = columns || [ |
| {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending} |
| ]; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| } |
| |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype = { |
| setDataSource: function(snapshot, nodeIndex) |
| { |
| this.snapshot = snapshot; |
| var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex); |
| var fakeEdge = { node: node }; |
| this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null)); |
| this.rootNode().sort(); |
| }, |
| |
| sortingChanged: function() |
| { |
| this.rootNode().sort(); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotContainmentDataGrid} |
| */ |
| WebInspector.HeapSnapshotRetainmentDataGrid = function() |
| { |
| this.showRetainingEdges = true; |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true} |
| ]; |
| WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns); |
| } |
| |
| WebInspector.HeapSnapshotRetainmentDataGrid.Events = { |
| ExpandRetainersComplete: "ExpandRetainersComplete" |
| } |
| |
| WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { |
| _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], |
| distance: ["_distance", sortAscending, "_name", true] |
| }[sortColumn]; |
| }, |
| |
| reset: function() |
| { |
| this.rootNode().removeChildren(); |
| this.resetSortingCache(); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotProxy} snapshot |
| * @param {number} nodeIndex |
| */ |
| setDataSource: function(snapshot, nodeIndex) |
| { |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); |
| |
| var dataGrid = this; |
| var maxExpandLevels = 20; |
| /** |
| * @this {!WebInspector.HeapSnapshotObjectNode} |
| */ |
| function populateComplete() |
| { |
| this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this); |
| this.expand(); |
| if (--maxExpandLevels > 0 && this.children.length > 0) { |
| var retainer = this.children[0]; |
| if (retainer._distance > 1) { |
| retainer.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, retainer); |
| retainer.populate(); |
| return; |
| } |
| } |
| dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete); |
| } |
| this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode()); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotViewportDataGrid} |
| */ |
| WebInspector.HeapSnapshotConstructorsDataGrid = function() |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true}, |
| {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} |
| ]; |
| WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); |
| this._profileIndex = -1; |
| this._topLevelNodes = []; |
| |
| this._objectIdToSelect = null; |
| } |
| |
| /** |
| * @constructor |
| * @param {number=} minNodeId |
| * @param {number=} maxNodeId |
| */ |
| WebInspector.HeapSnapshotConstructorsDataGrid.Request = function(minNodeId, maxNodeId) |
| { |
| if (typeof minNodeId === "number") { |
| this.key = minNodeId + ".." + maxNodeId; |
| this.filter = "function(node) { var id = node.id(); return id > " + minNodeId + " && id <= " + maxNodeId + "; }"; |
| } else { |
| this.key = "allObjects"; |
| this.filter = null; |
| } |
| } |
| |
| WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { |
| _sortFields: function(sortColumn, sortAscending) |
| { |
| return { |
| object: ["_name", sortAscending, "_count", false], |
| distance: ["_distance", sortAscending, "_retainedSize", true], |
| count: ["_count", sortAscending, "_name", true], |
| shallowSize: ["_shallowSize", sortAscending, "_name", true], |
| retainedSize: ["_retainedSize", sortAscending, "_name", true] |
| }[sortColumn]; |
| }, |
| |
| /** |
| * @override |
| * @param {HeapProfilerAgent.HeapSnapshotObjectId} id |
| */ |
| highlightObjectByHeapSnapshotId: function(id) |
| { |
| if (!this.snapshot) { |
| this._objectIdToSelect = id; |
| return; |
| } |
| |
| function didGetClassName(className) |
| { |
| var constructorNodes = this.topLevelNodes(); |
| for (var i = 0; i < constructorNodes.length; i++) { |
| var parent = constructorNodes[i]; |
| if (parent._name === className) { |
| parent.revealNodeBySnapshotObjectId(parseInt(id, 10)); |
| return; |
| } |
| } |
| } |
| this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this)); |
| }, |
| |
| setDataSource: function(snapshot) |
| { |
| this.snapshot = snapshot; |
| if (this._profileIndex === -1) |
| this._populateChildren(); |
| |
| if (this._objectIdToSelect) { |
| this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); |
| this._objectIdToSelect = null; |
| } |
| }, |
| |
| /** |
| * @param {number} minNodeId |
| * @param {number} maxNodeId |
| */ |
| setSelectionRange: function(minNodeId, maxNodeId) |
| { |
| this._populateChildren(new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId)); |
| }, |
| |
| _aggregatesReceived: function(key, aggregates) |
| { |
| this._requestInProgress = null; |
| if (this._nextRequest) { |
| this.snapshot.aggregates(false, this._nextRequest.key, this._nextRequest.filter, this._aggregatesReceived.bind(this, this._nextRequest.key)); |
| this._requestInProgress = this._nextRequest; |
| this._nextRequest = null; |
| } |
| this.dispose(); |
| this.removeTopLevelNodes(); |
| this.resetSortingCache(); |
| for (var constructor in aggregates) |
| this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key)); |
| this.sortingChanged(); |
| this._lastKey = key; |
| }, |
| |
| /** |
| * @param {WebInspector.HeapSnapshotConstructorsDataGrid.Request=} request |
| */ |
| _populateChildren: function(request) |
| { |
| request = request || new WebInspector.HeapSnapshotConstructorsDataGrid.Request(); |
| |
| if (this._requestInProgress) { |
| this._nextRequest = this._requestInProgress.key === request.key ? null : request; |
| return; |
| } |
| if (this._lastKey === request.key) |
| return; |
| this._requestInProgress = request; |
| this.snapshot.aggregates(false, request.key, request.filter, this._aggregatesReceived.bind(this, request.key)); |
| }, |
| |
| filterSelectIndexChanged: function(profiles, profileIndex) |
| { |
| this._profileIndex = profileIndex; |
| |
| var request = null; |
| if (profileIndex !== -1) { |
| var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; |
| var maxNodeId = profiles[profileIndex].maxJSObjectId; |
| request = new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId) |
| } |
| |
| this._populateChildren(request); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotViewportDataGrid} |
| */ |
| WebInspector.HeapSnapshotDiffDataGrid = function() |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, |
| {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true}, |
| {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true}, |
| {id: "countDelta", title: "# Delta", width: "64px", sortable: true}, |
| {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, |
| {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true}, |
| {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true} |
| ]; |
| WebInspector.HeapSnapshotViewportDataGrid.call(this, columns); |
| } |
| |
| WebInspector.HeapSnapshotDiffDataGrid.prototype = { |
| /** |
| * @override |
| * @return {number} |
| */ |
| defaultPopulateCount: function() |
| { |
| return 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(snapshot) |
| { |
| this.snapshot = snapshot; |
| }, |
| |
| /** |
| * @param {WebInspector.HeapSnapshotProxy} baseSnapshot |
| */ |
| setBaseDataSource: function(baseSnapshot) |
| { |
| this.baseSnapshot = baseSnapshot; |
| this.dispose(); |
| this.removeTopLevelNodes(); |
| this.resetSortingCache(); |
| if (this.baseSnapshot === this.snapshot) { |
| this.dispatchEventToListeners("sorting complete"); |
| return; |
| } |
| this._populateChildren(); |
| }, |
| |
| _populateChildren: function() |
| { |
| function aggregatesForDiffReceived(aggregatesForDiff) |
| { |
| this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); |
| function didCalculateSnapshotDiff(diffByClassName) |
| { |
| for (var className in diffByClassName) { |
| var diff = diffByClassName[className]; |
| this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff)); |
| } |
| this.sortingChanged(); |
| } |
| } |
| // Two snapshots live in different workers isolated from each other. That is why |
| // we first need to collect information about the nodes in the first snapshot and |
| // then pass it to the second snapshot to calclulate the diff. |
| this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this)); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotSortableDataGrid} |
| */ |
| WebInspector.HeapSnapshotDominatorsDataGrid = function() |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true} |
| ]; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); |
| this._objectIdToSelect = null; |
| } |
| |
| WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { |
| /** |
| * @override |
| * @return {number} |
| */ |
| defaultPopulateCount: function() |
| { |
| return 25; |
| }, |
| |
| setDataSource: function(snapshot) |
| { |
| this.snapshot = snapshot; |
| |
| var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex }; |
| this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode)); |
| this.rootNode().sort(); |
| |
| if (this._objectIdToSelect) { |
| this.highlightObjectByHeapSnapshotId(this._objectIdToSelect); |
| this._objectIdToSelect = null; |
| } |
| }, |
| |
| sortingChanged: function() |
| { |
| this.rootNode().sort(); |
| }, |
| |
| /** |
| * @override |
| * @param {HeapProfilerAgent.HeapSnapshotObjectId} id |
| */ |
| highlightObjectByHeapSnapshotId: function(id) |
| { |
| if (!this.snapshot) { |
| this._objectIdToSelect = id; |
| return; |
| } |
| |
| function didGetDominators(dominatorIds) |
| { |
| if (!dominatorIds) { |
| WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node")); |
| return; |
| } |
| var dominatorNode = this.rootNode(); |
| expandNextDominator.call(this, dominatorIds, dominatorNode); |
| } |
| |
| function expandNextDominator(dominatorIds, dominatorNode) |
| { |
| if (!dominatorNode) { |
| console.error("Cannot find dominator node"); |
| return; |
| } |
| if (!dominatorIds.length) { |
| this.highlightNode(dominatorNode); |
| dominatorNode.element.scrollIntoViewIfNeeded(true); |
| return; |
| } |
| var snapshotObjectId = dominatorIds.pop(); |
| dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds)); |
| } |
| |
| this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this)); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.DataGrid} |
| */ |
| WebInspector.AllocationDataGrid = function() |
| { |
| var columns = [ |
| {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, |
| {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}, |
| {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true}, |
| ]; |
| WebInspector.DataGrid.call(this, columns); |
| this._linkifier = new WebInspector.Linkifier(); |
| } |
| |
| WebInspector.AllocationDataGrid.prototype = { |
| setDataSource: function(snapshot) |
| { |
| this._snapshot = snapshot; |
| this._snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this)); |
| function didReceiveAllocationTracesTops(tops) |
| { |
| var root = this.rootNode(); |
| for (var i = 0; i < tops.length; i++) |
| root.appendChild(new WebInspector.AllocationGridNode(this, tops[i])); |
| } |
| }, |
| |
| __proto__: WebInspector.DataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.DataGridNode} |
| * @param {WebInspector.DataGrid} dataGrid |
| */ |
| WebInspector.AllocationGridNode = function(dataGrid, data) |
| { |
| WebInspector.DataGridNode.call(this, data, data.hasChildren); |
| this._dataGrid = dataGrid; |
| this._populated = false; |
| } |
| |
| WebInspector.AllocationGridNode.prototype = { |
| populate: function() |
| { |
| if (this._populated) |
| return; |
| this._populated = true; |
| this._dataGrid._snapshot.allocationNodeCallers(this.data.id, didReceiveCallers.bind(this)); |
| function didReceiveCallers(callers) |
| { |
| var callersChain = callers.nodesWithSingleCaller; |
| var parentNode = this; |
| for (var i = 0; i < callersChain.length; i++) { |
| var child = new WebInspector.AllocationGridNode(this._dataGrid, callersChain[i]); |
| parentNode.appendChild(child); |
| parentNode = child; |
| parentNode._populated = true; |
| if (this.expanded) |
| parentNode.expand(); |
| } |
| |
| var callersBranch = callers.branchingCallers; |
| for (var i = 0; i < callersBranch.length; i++) |
| parentNode.appendChild(new WebInspector.AllocationGridNode(this._dataGrid, callersBranch[i])); |
| } |
| }, |
| |
| /** |
| * @override |
| */ |
| expand: function() |
| { |
| WebInspector.DataGridNode.prototype.expand.call(this); |
| if (this.children.length === 1) |
| this.children[0].expand(); |
| }, |
| |
| /** |
| * @override |
| * @param {string} columnIdentifier |
| * @return {!Element} |
| */ |
| createCell: function(columnIdentifier) |
| { |
| var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); |
| |
| if (columnIdentifier !== "name") |
| return cell; |
| |
| var functionInfo = this.data; |
| if (functionInfo.scriptName) { |
| var urlElement = this._dataGrid._linkifier.linkifyLocation(functionInfo.scriptName, functionInfo.line - 1, functionInfo.column - 1, "profile-node-file"); |
| urlElement.style.maxWidth = "75%"; |
| cell.insertBefore(urlElement, cell.firstChild); |
| } |
| |
| return cell; |
| }, |
| |
| __proto__: WebInspector.DataGridNode.prototype |
| } |
| |