| /* |
| * 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} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns |
| */ |
| WebInspector.HeapSnapshotSortableDataGrid = function(dataDisplayDelegate, columns) |
| { |
| WebInspector.DataGrid.call(this, columns); |
| this._dataDisplayDelegate = dataDisplayDelegate; |
| |
| /** |
| * @type {number} |
| */ |
| this._recursiveSortingDepth = 0; |
| /** |
| * @type {?WebInspector.HeapSnapshotGridNode} |
| */ |
| this._highlightedNode = null; |
| /** |
| * @type {boolean} |
| */ |
| this._populatedAndSorted = false; |
| /** |
| * @type {?WebInspector.ToolbarInput} |
| */ |
| this._nameFilter = null; |
| this._nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(); |
| this.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this); |
| this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this); |
| } |
| |
| /** @enum {symbol} */ |
| WebInspector.HeapSnapshotSortableDataGrid.Events = { |
| ContentShown: Symbol("ContentShown"), |
| SortingComplete: Symbol("SortingComplete") |
| } |
| |
| WebInspector.HeapSnapshotSortableDataGrid.prototype = { |
| /** |
| * @return {!WebInspector.HeapSnapshotCommon.NodeFilter} |
| */ |
| nodeFilter: function() |
| { |
| return this._nodeFilter; |
| }, |
| |
| /** |
| * @param {!WebInspector.ToolbarInput} nameFilter |
| */ |
| setNameFilter: function(nameFilter) |
| { |
| this._nameFilter = nameFilter; |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| defaultPopulateCount: function() |
| { |
| return 100; |
| }, |
| |
| _disposeAllNodes: function() |
| { |
| var children = this.topLevelNodes(); |
| for (var i = 0, l = children.length; i < l; ++i) |
| children[i].dispose(); |
| }, |
| |
| /** |
| * @override |
| */ |
| wasShown: function() |
| { |
| if (this._nameFilter) { |
| this._nameFilter.addEventListener(WebInspector.ToolbarInput.Event.TextChanged, this._onNameFilterChanged, this); |
| this.updateVisibleNodes(true); |
| } |
| if (this._populatedAndSorted) |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); |
| }, |
| |
| _sortingComplete: function() |
| { |
| this.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this); |
| this._populatedAndSorted = true; |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this); |
| }, |
| |
| /** |
| * @override |
| */ |
| willHide: function() |
| { |
| if (this._nameFilter) |
| this._nameFilter.removeEventListener(WebInspector.ToolbarInput.Event.TextChanged, this._onNameFilterChanged, this); |
| this._clearCurrentHighlight(); |
| }, |
| |
| /** |
| * @param {!WebInspector.ContextMenu} contextMenu |
| * @param {!Event} event |
| */ |
| populateContextMenu: function(contextMenu, event) |
| { |
| var td = event.target.enclosingNodeOrSelfWithNodeName("td"); |
| if (!td) |
| return; |
| var node = td.heapSnapshotNode; |
| |
| /** |
| * @this {WebInspector.HeapSnapshotSortableDataGrid} |
| */ |
| function revealInSummaryView() |
| { |
| this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary"); |
| } |
| |
| if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode) |
| contextMenu.appendItem(WebInspector.UIString.capitalize("Reveal in Summary ^view"), revealInSummaryView.bind(this)); |
| }, |
| |
| resetSortingCache: function() |
| { |
| delete this._lastSortColumnIdentifier; |
| delete this._lastSortAscending; |
| }, |
| |
| /** |
| * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} |
| */ |
| topLevelNodes: function() |
| { |
| return this.rootNode().children; |
| }, |
| |
| /** |
| * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId |
| * @return {!Promise<?WebInspector.HeapSnapshotGridNode>} |
| */ |
| revealObjectByHeapSnapshotId: function(heapSnapshotObjectId) |
| { |
| return Promise.resolve(/** @type {?WebInspector.HeapSnapshotGridNode} */ (null)); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotGridNode} node |
| */ |
| highlightNode: function(node) |
| { |
| this._clearCurrentHighlight(); |
| this._highlightedNode = node; |
| WebInspector.runCSSAnimationOnce(this._highlightedNode.element(), "highlighted-row"); |
| }, |
| |
| nodeWasDetached: function(node) |
| { |
| if (this._highlightedNode === node) |
| this._clearCurrentHighlight(); |
| }, |
| |
| _clearCurrentHighlight: function() |
| { |
| if (!this._highlightedNode) |
| return |
| this._highlightedNode.element().classList.remove("highlighted-row"); |
| this._highlightedNode = null; |
| }, |
| |
| resetNameFilter: function() |
| { |
| this._nameFilter.setValue(""); |
| }, |
| |
| _onNameFilterChanged: function() |
| { |
| this.updateVisibleNodes(true); |
| }, |
| |
| 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.allChildren(this.rootNode()); |
| 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.recursiveSortingLeave(); |
| }, |
| |
| appendChildAfterSorting: function(child) |
| { |
| var revealed = child.revealed; |
| this.rootNode().appendChild(child); |
| child.revealed = revealed; |
| }, |
| |
| recursiveSortingEnter: function() |
| { |
| ++this._recursiveSortingDepth; |
| }, |
| |
| recursiveSortingLeave: function() |
| { |
| if (!this._recursiveSortingDepth) |
| return; |
| if (--this._recursiveSortingDepth) |
| return; |
| this.updateVisibleNodes(true); |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); |
| }, |
| |
| /** |
| * @param {boolean} force |
| */ |
| updateVisibleNodes: function(force) |
| { |
| }, |
| |
| /** |
| * @param {!WebInspector.DataGridNode} parent |
| * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} |
| */ |
| allChildren: function(parent) |
| { |
| return parent.children; |
| }, |
| |
| /** |
| * @param {!WebInspector.DataGridNode} parent |
| * @param {!WebInspector.DataGridNode} node |
| * @param {number} index |
| */ |
| insertChild: function(parent, node, index) |
| { |
| parent.insertChild(node, index); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotGridNode} parent |
| * @param {number} index |
| */ |
| removeChildByIndex: function(parent, index) |
| { |
| parent.removeChild(parent.children[index]); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotGridNode} parent |
| */ |
| removeAllChildren: function(parent) |
| { |
| parent.removeChildren(); |
| }, |
| |
| __proto__: WebInspector.DataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotSortableDataGrid} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns |
| */ |
| WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns) |
| { |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); |
| this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true); |
| this._topPaddingHeight = 0; |
| this._bottomPaddingHeight = 0; |
| } |
| |
| WebInspector.HeapSnapshotViewportDataGrid.prototype = { |
| /** |
| * @override |
| * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} |
| */ |
| topLevelNodes: function() |
| { |
| return this.allChildren(this.rootNode()); |
| }, |
| |
| appendChildAfterSorting: function(child) |
| { |
| // Do nothing here, it will be added in updateVisibleNodes. |
| }, |
| |
| /** |
| * @override |
| * @param {boolean} force |
| */ |
| updateVisibleNodes: function(force) |
| { |
| // Guard zone is used to ensure there are always some extra items |
| // above and below the viewport to support keyboard navigation. |
| var guardZoneHeight = 40; |
| var scrollHeight = this.scrollContainer.scrollHeight; |
| var scrollTop = this.scrollContainer.scrollTop; |
| var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight; |
| scrollTop = Math.max(0, scrollTop - guardZoneHeight); |
| scrollBottom = Math.max(0, scrollBottom - guardZoneHeight); |
| var viewPortHeight = scrollHeight - scrollTop - scrollBottom; |
| // Do nothing if populated nodes still fit the viewport. |
| if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight) |
| return; |
| var hysteresisHeight = 500; |
| scrollTop -= hysteresisHeight; |
| viewPortHeight += 2 * hysteresisHeight; |
| var selectedNode = this.selectedNode; |
| this.rootNode().removeChildren(); |
| |
| this._topPaddingHeight = 0; |
| this._bottomPaddingHeight = 0; |
| |
| this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight); |
| |
| this.setVerticalPadding(this._topPaddingHeight, this._bottomPaddingHeight); |
| |
| if (selectedNode) { |
| // Keep selection even if the node is not in the current viewport. |
| if (selectedNode.parent) |
| selectedNode.select(true); |
| else |
| this.selectedNode = selectedNode; |
| } |
| }, |
| |
| /** |
| * @param {!WebInspector.DataGridNode} parentNode |
| * @param {number} topBound |
| * @param {number} bottomBound |
| * @return {number} |
| */ |
| _addVisibleNodes: function(parentNode, topBound, bottomBound) |
| { |
| if (!parentNode.expanded) |
| return 0; |
| |
| var children = this.allChildren(parentNode); |
| var topPadding = 0; |
| var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : ""; |
| // Iterate over invisible nodes beyond the upper bound of viewport. |
| // Do not insert them into the grid, but count their total height. |
| for (var i = 0; i < children.length; ++i) { |
| var child = children[i]; |
| if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) |
| continue; |
| var newTop = topPadding + this._nodeHeight(child); |
| if (newTop > topBound) |
| break; |
| topPadding = newTop; |
| } |
| |
| // Put visible nodes into the data grid. |
| var position = topPadding; |
| for (; i < children.length && position < bottomBound; ++i) { |
| var child = children[i]; |
| if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) |
| continue; |
| var hasChildren = child.hasChildren; |
| child.removeChildren(); |
| child.hasChildren = hasChildren; |
| child.revealed = true; |
| parentNode.appendChild(child); |
| position += child.nodeSelfHeight(); |
| position += this._addVisibleNodes(child, topBound - position, bottomBound - position); |
| } |
| |
| // Count the invisible nodes beyond the bottom bound of the viewport. |
| var bottomPadding = 0; |
| for (; i < children.length; ++i) { |
| var child = children[i]; |
| if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue)) |
| continue; |
| bottomPadding += this._nodeHeight(child); |
| } |
| |
| this._topPaddingHeight += topPadding; |
| this._bottomPaddingHeight += bottomPadding; |
| return position + bottomPadding; |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotGridNode} node |
| * @return {number} |
| */ |
| _nodeHeight: function(node) |
| { |
| if (!node.revealed) |
| return 0; |
| var result = node.nodeSelfHeight(); |
| if (!node.expanded) |
| return result; |
| var children = this.allChildren(node); |
| for (var i = 0; i < children.length; i++) |
| result += this._nodeHeight(children[i]); |
| return result; |
| }, |
| |
| /** |
| * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal |
| * @return {!Promise.<!WebInspector.HeapSnapshotGridNode>} |
| */ |
| revealTreeNode: function(pathToReveal) |
| { |
| var height = this._calculateOffset(pathToReveal); |
| var node = /** @type {!WebInspector.HeapSnapshotGridNode} */(pathToReveal.peekLast()); |
| var scrollTop = this.scrollContainer.scrollTop; |
| var scrollBottom = scrollTop + this.scrollContainer.offsetHeight; |
| if (height >= scrollTop && height < scrollBottom) |
| return Promise.resolve(node); |
| |
| var scrollGap = 40; |
| this.scrollContainer.scrollTop = Math.max(0, height - scrollGap); |
| return new Promise(this._scrollTo.bind(this, node)); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotGridNode} node |
| * @param {function(!WebInspector.HeapSnapshotGridNode)} fulfill |
| */ |
| _scrollTo: function(node, fulfill) |
| { |
| console.assert(!this._scrollToResolveCallback); |
| this._scrollToResolveCallback = fulfill.bind(null, node); |
| }, |
| |
| /** |
| * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal |
| * @return {number} |
| */ |
| _calculateOffset: function(pathToReveal) |
| { |
| var parentNode = this.rootNode(); |
| var height = 0; |
| for (var i = 0; i < pathToReveal.length; ++i) { |
| var node = pathToReveal[i]; |
| var children = this.allChildren(parentNode); |
| for (var j = 0; j < children.length; ++j) { |
| var child = children[j]; |
| if (node === child) { |
| height += node.nodeSelfHeight(); |
| break; |
| } |
| height += this._nodeHeight(child); |
| } |
| parentNode = node; |
| } |
| return height - pathToReveal.peekLast().nodeSelfHeight(); |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.DataGridNode} parent |
| * @return {!Array.<!WebInspector.HeapSnapshotGridNode>} |
| */ |
| allChildren: function(parent) |
| { |
| return parent._allChildren || (parent._allChildren = []); |
| }, |
| |
| /** |
| * @param {!WebInspector.DataGridNode} parent |
| * @param {!WebInspector.HeapSnapshotGridNode} node |
| */ |
| appendNode: function(parent, node) |
| { |
| this.allChildren(parent).push(node); |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.DataGridNode} parent |
| * @param {!WebInspector.DataGridNode} node |
| * @param {number} index |
| */ |
| insertChild: function(parent, node, index) |
| { |
| this.allChildren(parent).splice(index, 0, /** @type {!WebInspector.HeapSnapshotGridNode} */(node)); |
| }, |
| |
| removeChildByIndex: function(parent, index) |
| { |
| this.allChildren(parent).splice(index, 1); |
| }, |
| |
| removeAllChildren: function(parent) |
| { |
| parent._allChildren = []; |
| }, |
| |
| removeTopLevelNodes: function() |
| { |
| this._disposeAllNodes(); |
| this.rootNode().removeChildren(); |
| this.rootNode()._allChildren = []; |
| }, |
| |
| /** |
| * @param {!Element} element |
| * @return {boolean} |
| */ |
| _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; |
| }, |
| |
| onResize: function() |
| { |
| WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this); |
| this.updateVisibleNodes(false); |
| }, |
| |
| _onScroll: function(event) |
| { |
| this.updateVisibleNodes(false); |
| |
| if (this._scrollToResolveCallback) { |
| this._scrollToResolveCallback(); |
| this._scrollToResolveCallback = null; |
| } |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotSortableDataGrid} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns |
| */ |
| WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns) |
| { |
| columns = columns || [ |
| {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "65px", sortable: true, fixedWidth: true}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "105px", sortable: true, fixedWidth: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "105px", sortable: true, fixedWidth: true, sort: WebInspector.DataGrid.Order.Descending} |
| ]; |
| WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns); |
| } |
| |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype = { |
| /** |
| * @param {!WebInspector.HeapSnapshotProxy} snapshot |
| * @param {number} nodeIndex |
| */ |
| setDataSource: function(snapshot, nodeIndex) |
| { |
| this.snapshot = snapshot; |
| var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex }; |
| var fakeEdge = { node: node }; |
| this.setRootNode(this._createRootNode(snapshot, fakeEdge)); |
| this.rootNode().sort(); |
| }, |
| |
| _createRootNode: function(snapshot, fakeEdge) |
| { |
| return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null); |
| }, |
| |
| sortingChanged: function() |
| { |
| var rootNode = this.rootNode(); |
| if (rootNode.hasChildren) |
| rootNode.sort(); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotContainmentDataGrid} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| */ |
| WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate) |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "65px", sortable: true, fixedWidth: true, sort: WebInspector.DataGrid.Order.Ascending}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "105px", sortable: true, fixedWidth: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "105px", sortable: true, fixedWidth: true} |
| ]; |
| WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns); |
| } |
| |
| /** @enum {symbol} */ |
| WebInspector.HeapSnapshotRetainmentDataGrid.Events = { |
| ExpandRetainersComplete: Symbol("ExpandRetainersComplete") |
| } |
| |
| WebInspector.HeapSnapshotRetainmentDataGrid.prototype = { |
| _createRootNode: function(snapshot, fakeEdge) |
| { |
| return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null); |
| }, |
| |
| _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(); |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.HeapSnapshotProxy} snapshot |
| * @param {number} nodeIndex |
| */ |
| setDataSource: function(snapshot, nodeIndex) |
| { |
| WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex); |
| this.rootNode().expand(); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotViewportDataGrid} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| */ |
| WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate) |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, |
| {id: "distance", title: WebInspector.UIString("Distance"), width: "65px", sortable: true, fixedWidth: true}, |
| {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true, fixedWidth: true}, |
| {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "105px", sortable: true, fixedWidth: true}, |
| {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "105px", sort: WebInspector.DataGrid.Order.Descending, sortable: true, fixedWidth: true} |
| ]; |
| WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); |
| this._profileIndex = -1; |
| |
| this._objectIdToSelect = 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 |
| * @return {!Promise<?WebInspector.HeapSnapshotGridNode>} |
| */ |
| revealObjectByHeapSnapshotId: function(id) |
| { |
| if (!this.snapshot) { |
| this._objectIdToSelect = id; |
| return Promise.resolve(/** @type {?WebInspector.HeapSnapshotGridNode} */ (null)); |
| } |
| |
| /** |
| * @param {!Array<!WebInspector.HeapSnapshotGridNode>} nodes |
| * @return {?Promise<!WebInspector.HeapSnapshotGridNode>} |
| * @this {WebInspector.HeapSnapshotConstructorsDataGrid} |
| */ |
| function didPopulateNode(nodes) |
| { |
| return nodes.length ? this.revealTreeNode(nodes) : null; |
| } |
| |
| /** |
| * @param {?string} className |
| * @return {?Promise<?WebInspector.HeapSnapshotGridNode>} |
| * @this {WebInspector.HeapSnapshotConstructorsDataGrid} |
| */ |
| function didGetClassName(className) |
| { |
| if (!className) |
| return null; |
| var constructorNodes = this.topLevelNodes(); |
| for (var i = 0; i < constructorNodes.length; i++) { |
| var parent = constructorNodes[i]; |
| if (parent._name === className) |
| return parent.populateNodeBySnapshotObjectId(parseInt(id, 10)).then(didPopulateNode.bind(this)); |
| } |
| // There are no visible top level nodes with such className. |
| return null; |
| } |
| return this.snapshot.nodeClassName(parseInt(id, 10)).then(didGetClassName.bind(this)); |
| }, |
| |
| clear: function() |
| { |
| this._nextRequestedFilter = null; |
| this._lastFilter = null; |
| this.removeTopLevelNodes(); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotProxy} snapshot |
| */ |
| setDataSource: function(snapshot) |
| { |
| this.snapshot = snapshot; |
| if (this._profileIndex === -1) |
| this._populateChildren(); |
| |
| if (this._objectIdToSelect) { |
| this.revealObjectByHeapSnapshotId(this._objectIdToSelect); |
| this._objectIdToSelect = null; |
| } |
| }, |
| |
| /** |
| * @param {number} minNodeId |
| * @param {number} maxNodeId |
| */ |
| setSelectionRange: function(minNodeId, maxNodeId) |
| { |
| this._nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId); |
| this._populateChildren(this._nodeFilter); |
| }, |
| |
| /** |
| * @param {number} allocationNodeId |
| */ |
| setAllocationNodeId: function(allocationNodeId) |
| { |
| this._nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(); |
| this._nodeFilter.allocationNodeId = allocationNodeId; |
| this._populateChildren(this._nodeFilter); |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter |
| * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates |
| */ |
| _aggregatesReceived: function(nodeFilter, aggregates) |
| { |
| this._filterInProgress = null; |
| if (this._nextRequestedFilter) { |
| this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter)); |
| this._filterInProgress = this._nextRequestedFilter; |
| this._nextRequestedFilter = null; |
| } |
| this.removeTopLevelNodes(); |
| this.resetSortingCache(); |
| for (var constructor in aggregates) |
| this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter)); |
| this.sortingChanged(); |
| this._lastFilter = nodeFilter; |
| }, |
| |
| /** |
| * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter |
| */ |
| _populateChildren: function(nodeFilter) |
| { |
| nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter(); |
| |
| if (this._filterInProgress) { |
| this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter; |
| return; |
| } |
| if (this._lastFilter && this._lastFilter.equals(nodeFilter)) |
| return; |
| this._filterInProgress = nodeFilter; |
| this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter)); |
| }, |
| |
| filterSelectIndexChanged: function(profiles, profileIndex) |
| { |
| this._profileIndex = profileIndex; |
| this._nodeFilter = undefined; |
| if (profileIndex !== -1) { |
| var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0; |
| var maxNodeId = profiles[profileIndex].maxJSObjectId; |
| this._nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId); |
| } |
| |
| this._populateChildren(this._nodeFilter); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotViewportDataGrid} |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| */ |
| WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate) |
| { |
| var columns = [ |
| {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true}, |
| {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true, fixedWidth: true}, |
| {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true, fixedWidth: true}, |
| {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true, fixedWidth: true}, |
| {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, fixedWidth: true, sort: WebInspector.DataGrid.Order.Descending}, |
| {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true, fixedWidth: true}, |
| {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true, fixedWidth: true} |
| ]; |
| WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, 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.removeTopLevelNodes(); |
| this.resetSortingCache(); |
| if (this.baseSnapshot === this.snapshot) { |
| this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete); |
| return; |
| } |
| this._populateChildren(); |
| }, |
| |
| _populateChildren: function() |
| { |
| /** |
| * @this {WebInspector.HeapSnapshotDiffDataGrid} |
| */ |
| function aggregatesForDiffReceived(aggregatesForDiff) |
| { |
| this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this)); |
| |
| /** |
| * @this {WebInspector.HeapSnapshotDiffDataGrid} |
| */ |
| function didCalculateSnapshotDiff(diffByClassName) |
| { |
| for (var className in diffByClassName) { |
| var diff = diffByClassName[className]; |
| this.appendNode(this.rootNode(), 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.HeapSnapshotViewportDataGrid} |
| * @param {?WebInspector.Target} target |
| * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate |
| */ |
| WebInspector.AllocationDataGrid = function(target, dataDisplayDelegate) |
| { |
| var columns = [ |
| {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true, fixedWidth: true}, |
| {id: "count", title: WebInspector.UIString("Count"), width: "60px", sortable: true, fixedWidth: true}, |
| {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true, fixedWidth: true}, |
| {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, fixedWidth: true, sort: WebInspector.DataGrid.Order.Descending}, |
| {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true}, |
| ]; |
| WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns); |
| this._target = target; |
| this._linkifier = new WebInspector.Linkifier(); |
| } |
| |
| WebInspector.AllocationDataGrid.prototype = { |
| |
| /** |
| * @return {?WebInspector.Target} |
| */ |
| target: function() |
| { |
| return this._target; |
| }, |
| |
| dispose: function() |
| { |
| this._linkifier.reset(); |
| }, |
| |
| setDataSource: function(snapshot) |
| { |
| this.snapshot = snapshot; |
| this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this)); |
| |
| /** |
| * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops |
| * @this {WebInspector.AllocationDataGrid} |
| */ |
| function didReceiveAllocationTracesTops(tops) |
| { |
| this._topNodes = tops; |
| this._populateChildren(); |
| } |
| }, |
| |
| _populateChildren: function() |
| { |
| this.removeTopLevelNodes(); |
| var root = this.rootNode(); |
| var tops = this._topNodes; |
| for (var i = 0; i < tops.length; i++) |
| this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i])); |
| this.updateVisibleNodes(true); |
| }, |
| |
| sortingChanged: function() |
| { |
| this._topNodes.sort(this._createComparator()); |
| this.rootNode().removeChildren(); |
| this._populateChildren(); |
| }, |
| |
| |
| /** |
| * @return {function(!Object, !Object):number} |
| */ |
| _createComparator: function() |
| { |
| var fieldName = this.sortColumnIdentifier(); |
| var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1; |
| /** |
| * @param {!Object} a |
| * @param {!Object} b |
| * @return {number} |
| */ |
| function compare(a, b) |
| { |
| if (a[fieldName] > b[fieldName]) |
| return compareResult; |
| if (a[fieldName] < b[fieldName]) |
| return -compareResult; |
| return 0; |
| } |
| return compare; |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype |
| } |