monaca-lib
Version:
Monaca cloud API bindings for JavaScript
1,457 lines (1,275 loc) • 74.1 kB
JavaScript
/*
* 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.
*/
/**
* @constructor
* @implements {WebInspector.ProfileType.DataDisplayDelegate}
* @implements {WebInspector.Searchable}
* @extends {WebInspector.VBox}
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @param {!WebInspector.HeapProfileHeader} profile
*/
WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
{
WebInspector.VBox.call(this);
this.element.classList.add("heap-snapshot-view");
profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
if (profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
}
this._parentDataDisplayDelegate = dataDisplayDelegate;
this._searchableView = new WebInspector.SearchableView(this);
this._searchableView.show(this.element);
this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
this._splitView.show(this._searchableView.element);
this._containmentView = new WebInspector.VBox();
this._containmentView.setMinimumSize(50, 25);
this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(this);
this._containmentDataGrid.show(this._containmentView.element);
this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
this._constructorsView = new WebInspector.VBox();
this._constructorsView.setMinimumSize(50, 25);
this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(this);
this._constructorsDataGrid.show(this._constructorsView.element);
this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
this._diffView = new WebInspector.VBox();
this._diffView.setMinimumSize(50, 25);
this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(this);
this._diffDataGrid.show(this._diffView.element);
this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
if (profile._hasAllocationStacks) {
this._allocationView = new WebInspector.VBox();
this._allocationView.setMinimumSize(50, 25);
this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.target() , this);
this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
this._allocationDataGrid.show(this._allocationView.element);
this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.target());
this._allocationStackView.setMinimumSize(50, 25);
this._tabbedPane = new WebInspector.TabbedPane();
this._tabbedPane.setCloseableTabs(false);
this._tabbedPane.headerElement().classList.add("heap-object-details-header");
}
this._retainmentView = new WebInspector.VBox();
this._retainmentView.setMinimumSize(50, 21);
this._retainmentView.element.classList.add("retaining-paths-view");
var splitViewResizer;
if (this._allocationStackView) {
this._tabbedPane = new WebInspector.TabbedPane();
this._tabbedPane.setCloseableTabs(false);
this._tabbedPane.headerElement().classList.add("heap-object-details-header");
this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
splitViewResizer = this._tabbedPane.headerElement();
this._objectDetailsView = this._tabbedPane;
} else {
var retainmentViewHeader = createElementWithClass("div", "heap-snapshot-view-resizer");
var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
this._retainmentView.element.appendChild(retainmentViewHeader);
splitViewResizer = retainmentViewHeader;
this._objectDetailsView = this._retainmentView;
}
this._splitView.hideDefaultResizer();
this._splitView.installResizer(splitViewResizer);
this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(this);
this._retainmentDataGrid.show(this._retainmentView.element);
this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
this._retainmentDataGrid.reset();
this._perspectives = [];
this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
if (this._allocationView)
this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
for (var i = 0; i < this._perspectives.length; ++i)
this._perspectiveSelect.createOption(this._perspectives[i].title());
this._profile = profile;
this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
this._baseSelect.setVisible(false);
this._updateBaseOptions();
this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
this._filterSelect.setVisible(false);
this._updateFilterOptions();
this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
this._classNameFilter.setVisible(false);
this._constructorsDataGrid.setNameFilter(this._classNameFilter);
this._diffDataGrid.setNameFilter(this._classNameFilter);
this._selectedSizeText = new WebInspector.StatusBarText("");
this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
this._currentPerspectiveIndex = 0;
this._currentPerspective = this._perspectives[0];
this._currentPerspective.activate(this);
this._dataGrid = this._currentPerspective.masterGrid(this);
this._refreshView();
this._searchThrottler = new WebInspector.Throttler(0);
}
/**
* @constructor
* @param {string} title
*/
WebInspector.HeapSnapshotView.Perspective = function(title)
{
this._title = title;
}
WebInspector.HeapSnapshotView.Perspective.prototype = {
/**
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView) { },
/**
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
deactivate: function(heapSnapshotView)
{
heapSnapshotView._baseSelect.setVisible(false);
heapSnapshotView._filterSelect.setVisible(false);
heapSnapshotView._classNameFilter.setVisible(false);
if (heapSnapshotView._trackingOverviewGrid)
heapSnapshotView._trackingOverviewGrid.detach();
if (heapSnapshotView._allocationView)
heapSnapshotView._allocationView.detach();
if (heapSnapshotView._statisticsView)
heapSnapshotView._statisticsView.detach();
heapSnapshotView._splitView.detach();
heapSnapshotView._splitView.detachChildViews();
},
/**
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return null;
},
/**
* @return {string}
*/
title: function()
{
return this._title;
},
/**
* @return {boolean}
*/
supportsSearch: function()
{
return false;
}
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotView.Perspective}
*/
WebInspector.HeapSnapshotView.SummaryPerspective = function()
{
WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
}
WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView)
{
heapSnapshotView._splitView.setMainView(heapSnapshotView._constructorsView);
heapSnapshotView._splitView.setSidebarView(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitView.show(heapSnapshotView._searchableView.element);
heapSnapshotView._filterSelect.setVisible(true);
heapSnapshotView._classNameFilter.setVisible(true);
if (heapSnapshotView._trackingOverviewGrid) {
heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView._searchableView.element, heapSnapshotView._splitView.element);
heapSnapshotView._trackingOverviewGrid.update();
heapSnapshotView._trackingOverviewGrid._updateGrid();
}
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return heapSnapshotView._constructorsDataGrid;
},
/**
* @override
* @return {boolean}
*/
supportsSearch: function()
{
return true;
},
__proto__: WebInspector.HeapSnapshotView.Perspective.prototype
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotView.Perspective}
*/
WebInspector.HeapSnapshotView.ComparisonPerspective = function()
{
WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
}
WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView)
{
heapSnapshotView._splitView.setMainView(heapSnapshotView._diffView);
heapSnapshotView._splitView.setSidebarView(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitView.show(heapSnapshotView._searchableView.element);
heapSnapshotView._baseSelect.setVisible(true);
heapSnapshotView._classNameFilter.setVisible(true);
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return heapSnapshotView._diffDataGrid;
},
/**
* @override
* @return {boolean}
*/
supportsSearch: function()
{
return true;
},
__proto__: WebInspector.HeapSnapshotView.Perspective.prototype
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotView.Perspective}
*/
WebInspector.HeapSnapshotView.ContainmentPerspective = function()
{
WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
}
WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView)
{
heapSnapshotView._splitView.setMainView(heapSnapshotView._containmentView);
heapSnapshotView._splitView.setSidebarView(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitView.show(heapSnapshotView._searchableView.element);
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return heapSnapshotView._containmentDataGrid;
},
__proto__: WebInspector.HeapSnapshotView.Perspective.prototype
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotView.Perspective}
*/
WebInspector.HeapSnapshotView.AllocationPerspective = function()
{
WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
this._allocationSplitView.setSidebarView(new WebInspector.VBox());
var resizer = createElementWithClass("div", "heap-snapshot-view-resizer");
var title = resizer.createChild("div", "title").createChild("span");
title.textContent = WebInspector.UIString("Live objects");
this._allocationSplitView.hideDefaultResizer();
this._allocationSplitView.installResizer(resizer);
this._allocationSplitView.sidebarView().element.appendChild(resizer);
}
WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView)
{
this._allocationSplitView.setMainView(heapSnapshotView._allocationView);
heapSnapshotView._splitView.setMainView(heapSnapshotView._constructorsView);
heapSnapshotView._splitView.setSidebarView(heapSnapshotView._objectDetailsView);
this._allocationSplitView.setSidebarView(heapSnapshotView._splitView);
this._allocationSplitView.show(heapSnapshotView._searchableView.element);
heapSnapshotView._constructorsDataGrid.clear();
var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
if (selectedNode)
heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
deactivate: function(heapSnapshotView)
{
this._allocationSplitView.detach();
WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return heapSnapshotView._allocationDataGrid;
},
__proto__: WebInspector.HeapSnapshotView.Perspective.prototype
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotView.Perspective}
*/
WebInspector.HeapSnapshotView.StatisticsPerspective = function()
{
WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
}
WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
*/
activate: function(heapSnapshotView)
{
heapSnapshotView._statisticsView.show(heapSnapshotView._searchableView.element);
},
/**
* @override
* @param {!WebInspector.HeapSnapshotView} heapSnapshotView
* @return {?WebInspector.DataGrid}
*/
masterGrid: function(heapSnapshotView)
{
return null;
},
__proto__: WebInspector.HeapSnapshotView.Perspective.prototype
}
WebInspector.HeapSnapshotView.prototype = {
/**
* @return {!WebInspector.SearchableView}
*/
searchableView: function()
{
return this._searchableView;
},
/**
* @override
* @param {?WebInspector.ProfileHeader} profile
* @return {?WebInspector.View}
*/
showProfile: function(profile)
{
return this._parentDataDisplayDelegate.showProfile(profile);
},
/**
* @override
* @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
* @param {string} perspectiveName
*/
showObject: function(snapshotObjectId, perspectiveName)
{
if (snapshotObjectId <= this._profile.maxJSObjectId)
this.selectLiveObject(perspectiveName, snapshotObjectId);
else
this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName);
},
_refreshView: function()
{
this._profile._loadPromise.then(profileCallback.bind(this));
/**
* @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
* @this {WebInspector.HeapSnapshotView}
*/
function profileCallback(heapSnapshotProxy)
{
heapSnapshotProxy.getStatistics().then(this._gotStatistics.bind(this));
var list = this._profiles();
var profileIndex = list.indexOf(this._profile);
this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
this._dataGrid.setDataSource(heapSnapshotProxy);
if (this._trackingOverviewGrid)
this._trackingOverviewGrid._updateGrid();
}
},
/**
* @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
*/
_gotStatistics: function(statistics)
{
this._statisticsView.setTotal(statistics.total);
this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
this._statisticsView.addRecord(statistics.system, WebInspector.UIString("System Objects"), "#98f");
this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
},
_onIdsRangeChanged: function(event)
{
var minId = event.data.minId;
var maxId = event.data.maxId;
this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
if (this._constructorsDataGrid.snapshot)
this._constructorsDataGrid.setSelectionRange(minId, maxId);
},
/**
* @return {!Array.<!WebInspector.StatusBarItem>}
*/
statusBarItems: function()
{
var result = [this._perspectiveSelect, this._classNameFilter];
if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
result.push(this._baseSelect, this._filterSelect);
result.push(this._selectedSizeText);
return result;
},
wasShown: function()
{
this._profile._loadPromise.then(this._profile._wasShown.bind(this._profile));
},
willHide: function()
{
this._currentSearchResultIndex = -1;
this._popoverHelper.hidePopover();
if (this.helpPopover && this.helpPopover.isShowing())
this.helpPopover.hide();
},
/**
* @override
* @return {boolean}
*/
supportsCaseSensitiveSearch: function()
{
return true;
},
/**
* @override
* @return {boolean}
*/
supportsRegexSearch: function()
{
return false;
},
/**
* @override
*/
searchCanceled: function()
{
this._currentSearchResultIndex = -1;
this._searchResults = [];
},
/**
* @param {function()} callback
* @param {?WebInspector.HeapSnapshotGridNode} node
*/
_selectRevealedNode: function(callback, node)
{
if (node)
node.select();
callback();
},
/**
* @override
* @param {!WebInspector.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch: function(searchConfig, shouldJump, jumpBackwards)
{
var nextQuery = new WebInspector.HeapSnapshotCommon.SearchConfig(
searchConfig.query.trim(),
searchConfig.caseSensitive,
searchConfig.isRegex,
shouldJump,
jumpBackwards || false
);
this._searchThrottler.schedule(this._performSearch.bind(this, nextQuery));
},
/**
* @param {!WebInspector.HeapSnapshotCommon.SearchConfig} nextQuery
* @param {function()} callback
*/
_performSearch: function(nextQuery, callback)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
if (!this._currentPerspective.supportsSearch()) {
callback();
return;
}
this.currentQuery = nextQuery;
var query = nextQuery.query.trim();
if (!query) {
callback();
return;
}
if (query.charAt(0) === "@") {
var snapshotNodeId = parseInt(query.substring(1), 10);
if (!isNaN(snapshotNodeId))
this._dataGrid.revealObjectByHeapSnapshotId(String(snapshotNodeId), this._selectRevealedNode.bind(this, callback));
else
callback();
return;
}
/**
* @this {WebInspector.HeapSnapshotView}
*/
function didSearch(entryIds)
{
this._searchResults = entryIds;
this._searchableView.updateSearchMatchesCount(this._searchResults.length);
if (this._searchResults.length)
this._currentSearchResultIndex = nextQuery.jumpBackwards ? this._searchResults.length - 1 : 0;
this._jumpToSearchResult(this._currentSearchResultIndex, callback);
}
this._profile._snapshotProxy.search(this.currentQuery, this._dataGrid.nodeFilter(), didSearch.bind(this));
},
/**
* @override
*/
jumpToNextSearchResult: function()
{
if (!this._searchResults.length)
return;
this._currentSearchResultIndex = (this._currentSearchResultIndex + 1) % this._searchResults.length;
this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
},
/**
* @override
*/
jumpToPreviousSearchResult: function()
{
if (!this._searchResults.length)
return;
this._currentSearchResultIndex = (this._currentSearchResultIndex + this._searchResults.length - 1) % this._searchResults.length;
this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
},
_jumpToSearchResult: function(searchResultIndex, callback)
{
this._dataGrid.revealObjectByHeapSnapshotId(String(this._searchResults[searchResultIndex]), this._selectRevealedNode.bind(this, callback));
this._searchableView.updateCurrentMatchIndex(searchResultIndex);
},
refreshVisibleData: function()
{
if (!this._dataGrid)
return;
var child = this._dataGrid.rootNode().children[0];
while (child) {
child.refresh();
child = child.traverseNextNode(false, null, true);
}
},
_changeBase: function()
{
if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
return;
this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
// Change set base data source only if main data source is already set.
if (dataGrid.snapshot)
this._baseProfile._loadPromise.then(dataGrid.setBaseDataSource.bind(dataGrid));
if (!this.currentQuery || !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.performSearch(this.currentQuery, false);
},
_changeFilter: function()
{
var profileIndex = this._filterSelect.selectedIndex() - 1;
this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
label: this._filterSelect.selectedOption().label
});
if (!this.currentQuery || !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.performSearch(this.currentQuery, false);
},
/**
* @return {!Array.<!WebInspector.ProfileHeader>}
*/
_profiles: function()
{
return this._profile.profileType().getProfiles();
},
/**
* @param {!WebInspector.ContextMenu} contextMenu
* @param {!Event} event
*/
populateContextMenu: function(contextMenu, event)
{
if (this._dataGrid)
this._dataGrid.populateContextMenu(contextMenu, event);
},
_selectionChanged: function(event)
{
var selectedNode = event.target.selectedNode;
this._setSelectedNodeForDetailsView(selectedNode);
this._inspectedObjectChanged(event);
},
_onSelectAllocationNode: function(event)
{
var selectedNode = event.target.selectedNode;
this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
this._setSelectedNodeForDetailsView(null);
},
_inspectedObjectChanged: function(event)
{
var selectedNode = event.target.selectedNode;
var target = this._profile.target();
if (target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
target.consoleAgent().addInspectedHeapObject(selectedNode.snapshotNodeId);
},
/**
* @param {?WebInspector.HeapSnapshotGridNode} nodeItem
*/
_setSelectedNodeForDetailsView: function(nodeItem)
{
var dataSource = nodeItem && nodeItem.retainersDataSource();
if (dataSource) {
this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
if (this._allocationStackView)
this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex);
} else {
if (this._allocationStackView)
this._allocationStackView.clear();
this._retainmentDataGrid.reset();
}
},
/**
* @param {string} perspectiveTitle
* @param {function()} callback
*/
_changePerspectiveAndWait: function(perspectiveTitle, callback)
{
var perspectiveIndex = null;
for (var i = 0; i < this._perspectives.length; ++i) {
if (this._perspectives[i].title() === perspectiveTitle) {
perspectiveIndex = i;
break;
}
}
if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
setTimeout(callback, 0);
return;
}
/**
* @this {WebInspector.HeapSnapshotView}
*/
function dataGridContentShown(event)
{
var dataGrid = event.data;
dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
if (dataGrid === this._dataGrid)
callback();
}
this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
this._changePerspective(perspectiveIndex);
},
_updateDataSourceAndView: function()
{
var dataGrid = this._dataGrid;
if (!dataGrid || dataGrid.snapshot)
return;
this._profile._loadPromise.then(didLoadSnapshot.bind(this));
/**
* @this {WebInspector.HeapSnapshotView}
*/
function didLoadSnapshot(snapshotProxy)
{
if (this._dataGrid !== dataGrid)
return;
if (dataGrid.snapshot !== snapshotProxy)
dataGrid.setDataSource(snapshotProxy);
if (dataGrid === this._diffDataGrid) {
if (!this._baseProfile)
this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
this._baseProfile._loadPromise.then(didLoadBaseSnaphot.bind(this));
}
}
/**
* @this {WebInspector.HeapSnapshotView}
*/
function didLoadBaseSnaphot(baseSnapshotProxy)
{
if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
}
},
_onSelectedPerspectiveChanged: function(event)
{
this._changePerspective(event.target.selectedIndex);
// FIXME: This is needed by CodeSchool extension.
this._onSelectedViewChanged(event);
},
_onSelectedViewChanged: function(event)
{
},
/**
* @param {number} selectedIndex
*/
_changePerspective: function(selectedIndex)
{
if (selectedIndex === this._currentPerspectiveIndex)
return;
this._currentPerspectiveIndex = selectedIndex;
this._currentPerspective.deactivate(this);
var perspective = this._perspectives[selectedIndex];
this._currentPerspective = perspective;
this._dataGrid = perspective.masterGrid(this);
perspective.activate(this);
this.refreshVisibleData();
if (this._dataGrid)
this._dataGrid.updateWidths();
this._updateDataSourceAndView();
if (!this.currentQuery || !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.performSearch(this.currentQuery, false);
},
/**
* @param {string} perspectiveName
* @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
*/
selectLiveObject: function(perspectiveName, snapshotObjectId)
{
this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
/**
* @this {WebInspector.HeapSnapshotView}
*/
function didChangePerspective()
{
this._dataGrid.revealObjectByHeapSnapshotId(snapshotObjectId, didRevealObject);
}
/**
* @param {?WebInspector.HeapSnapshotGridNode} node
*/
function didRevealObject(node)
{
if (node)
node.select();
else
WebInspector.console.error("Cannot find corresponding heap snapshot node");
}
},
_getHoverAnchor: function(target)
{
var span = target.enclosingNodeOrSelfWithNodeName("span");
if (!span)
return;
var row = target.enclosingNodeOrSelfWithNodeName("tr");
if (!row)
return;
span.node = row._dataGridNode;
return span;
},
_resolveObjectForPopover: function(element, showCallback, objectGroupName)
{
if (!this._profile.target())
return;
if (!element.node)
return;
element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName);
},
_updateBaseOptions: function()
{
var list = this._profiles();
// We're assuming that snapshots can only be added.
if (this._baseSelect.size() === list.length)
return;
for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
var title = list[i].title;
this._baseSelect.createOption(title);
}
},
_updateFilterOptions: function()
{
var list = this._profiles();
// We're assuming that snapshots can only be added.
if (this._filterSelect.size() - 1 === list.length)
return;
if (!this._filterSelect.size())
this._filterSelect.createOption(WebInspector.UIString("All objects"));
for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
var title = list[i].title;
if (!i)
title = WebInspector.UIString("Objects allocated before %s", title);
else
title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
this._filterSelect.createOption(title);
}
},
_updateControls: function()
{
this._updateBaseOptions();
this._updateFilterOptions();
},
/**
* @param {!WebInspector.Event} event
*/
_onReceiveSnapshot: function(event)
{
this._updateControls();
},
/**
* @param {!WebInspector.Event} event
*/
_onProfileHeaderRemoved: function(event)
{
var profile = event.data;
if (this._profile === profile) {
this.detach();
this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
this.dispose();
} else {
this._updateControls();
}
},
dispose: function()
{
if (this._allocationStackView) {
this._allocationStackView.clear();
this._allocationDataGrid.dispose();
}
if (this._trackingOverviewGrid)
this._trackingOverviewGrid.dispose();
},
__proto__: WebInspector.VBox.prototype
}
/**
* @constructor
* @extends {WebInspector.ProfileType}
* @implements {WebInspector.TargetManager.Observer}
* @param {string=} id
* @param {string=} title
*/
WebInspector.HeapSnapshotProfileType = function(id, title)
{
WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
WebInspector.targetManager.observeTargets(this);
WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this);
WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this);
}
WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
WebInspector.HeapSnapshotProfileType.prototype = {
/**
* @override
* @param {!WebInspector.Target} target
*/
targetAdded: function(target)
{
target.heapProfilerModel.enable();
},
/**
* @override
* @param {!WebInspector.Target} target
*/
targetRemoved: function(target)
{
},
/**
* @override
* @return {string}
*/
fileExtension: function()
{
return ".heapsnapshot";
},
get buttonTooltip()
{
return WebInspector.UIString("Take heap snapshot.");
},
/**
* @override
* @return {boolean}
*/
isInstantProfile: function()
{
return true;
},
/**
* @override
* @return {boolean}
*/
buttonClicked: function()
{
this._takeHeapSnapshot(function() {});
WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
return false;
},
get treeItemTitle()
{
return WebInspector.UIString("HEAP SNAPSHOTS");
},
get description()
{
return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
},
/**
* @override
* @param {string} title
* @return {!WebInspector.ProfileHeader}
*/
createProfileLoadedFromFile: function(title)
{
return new WebInspector.HeapProfileHeader(null, this, title);
},
_takeHeapSnapshot: function(callback)
{
if (this.profileBeingRecorded())
return;
var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target));
var profile = new WebInspector.HeapProfileHeader(target, this);
this.setProfileBeingRecorded(profile);
this.addProfile(profile);
profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
/**
* @param {?string} error
* @this {WebInspector.HeapSnapshotProfileType}
*/
function didTakeHeapSnapshot(error)
{
var profile = this._profileBeingRecorded;
profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
profile._finishLoad();
this.setProfileBeingRecorded(null);
this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
callback();
}
target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
},
/**
* @param {!WebInspector.Event} event
*/
_addHeapSnapshotChunk: function(event)
{
if (!this.profileBeingRecorded())
return;
var chunk = /** @type {string} */(event.data);
this.profileBeingRecorded().transferChunk(chunk);
},
/**
* @param {!WebInspector.Event} event
*/
_reportHeapSnapshotProgress: function(event)
{
var profile = this.profileBeingRecorded();
if (!profile)
return;
var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data);
profile.updateStatus(WebInspector.UIString("%.0f%", (data.done / data.total) * 100), true);
if (data.finished)
profile._prepareToLoad();
},
_resetProfiles: function()
{
this._reset();
},
_snapshotReceived: function(profile)
{
if (this._profileBeingRecorded === profile)
this.setProfileBeingRecorded(null);
this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
},
__proto__: WebInspector.ProfileType.prototype
}
/**
* @constructor
* @extends {WebInspector.HeapSnapshotProfileType}
*/
WebInspector.TrackingHeapSnapshotProfileType = function()
{
WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
}
WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
WebInspector.TrackingHeapSnapshotProfileType.prototype = {
/**
* @override
* @param {!WebInspector.Target} target
*/
targetAdded: function(target)
{
WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target);
target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
},
/**
* @override
* @param {!WebInspector.Target} target
*/
targetRemoved: function(target)
{
WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target);
target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
},
/**
* @param {!WebInspector.Event} event
*/
_heapStatsUpdate: function(event)
{
if (!this._profileSamples)
return;
var samples = /** @type {!Array.<number>} */ (event.data);
var index;
for (var i = 0; i < samples.length; i += 3) {
index = samples[i];
var size = samples[i+2];
this._profileSamples.sizes[index] = size;
if (!this._profileSamples.max[index])
this._profileSamples.max[index] = size;
}
},
/**
* @param {!WebInspector.Event} event
*/
_lastSeenObjectId: function(event)
{
var profileSamples = this._profileSamples;
if (!profileSamples)
return;
var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data);
var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
profileSamples.ids[currentIndex] = data.lastSeenObjectId;
if (!profileSamples.max[currentIndex]) {
profileSamples.max[currentIndex] = 0;
profileSamples.sizes[currentIndex] = 0;
}
profileSamples.timestamps[currentIndex] = data.timestamp;
if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0])
profileSamples.totalTime *= 2;
this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
this._profileBeingRecorded.updateStatus(null, true);
},
/**
* @override
* @return {boolean}
*/
hasTemporaryView: function()
{
return true;
},
get buttonTooltip()
{
return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
},
/**
* @override
* @return {boolean}
*/
isInstantProfile: function()
{
return false;
},
/**
* @override
* @return {boolean}
*/
buttonClicked: function()
{
return this._toggleRecording();
},
_startRecordingProfile: function()
{
if (this.profileBeingRecorded())
return;
var recordAllocationStacks = WebInspector.settings.recordAllocationStacks.get();
this._addNewProfile(recordAllocationStacks);
this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks);
},
/**
* @param {boolean} withAllocationStacks
*/
_addNewProfile: function(withAllocationStacks)
{
var target = WebInspector.context.flavor(WebInspector.Target);
this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined, withAllocationStacks));
this._lastSeenIndex = -1;
this._profileSamples = {
'sizes': [],
'ids': [],
'timestamps': [],
'max': [],
'totalTime': 30000
};
this._profileBeingRecorded._profileSamples = this._profileSamples;
this._recording = true;
this.addProfile(this._profileBeingRecorded);
this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
},
_stopRecordingProfile: function()
{
this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
/**
* @param {?string} error
* @this {WebInspector.HeapSnapshotProfileType}
*/
function didTakeHeapSnapshot(error)
{
var profile = this.profileBeingRecorded();
if (!profile)
return;
profile._finishLoad();
this._profileSamples = null;
this.setProfileBeingRecorded(null);
this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
}
this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
this._recording = false;
this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
},
_toggleRecording: function()
{
if (this._recording)
this._stopRecordingProfile();
else
this._startRecordingProfile();
return this._recording;
},
get treeItemTitle()
{
return WebInspector.UIString("HEAP TIMELINES");
},
get description()
{
return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
},
/**
* @override
*/
_resetProfiles: function()
{
var wasRecording = this._recording;
var recordingAllocationStacks = wasRecording && this.profileBeingRecorded()._hasAllocationStacks;
// Clear current profile to avoid stopping backend.
this.setProfileBeingRecorded(null);
WebInspector.HeapSnapshotProfileType.prototype._resetProfiles.call(this);
this._profileSamples = null;
this._lastSeenIndex = -1;
if (wasRecording)
this._addNewProfile(recordingAllocationStacks);
},
/**
* @override
*/
profileBeingRecordedRemoved: function()
{
this._stopRecordingProfile();
this._profileSamples = null;
},
__proto__: WebInspector.HeapSnapshotProfileType.prototype
}
/**
* @constructor
* @extends {WebInspector.ProfileHeader}
* @param {?WebInspector.Target} target
* @param {!WebInspector.HeapSnapshotProfileType} type
* @param {string=} title
* @param {boolean=} hasAllocationStacks
*/
WebInspector.HeapProfileHeader = function(target, type, title, hasAllocationStacks)
{
WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid()));
this._hasAllocationStacks = !!hasAllocationStacks;
this.maxJSObjectId = -1;
/**
* @type {?WebInspector.HeapSnapshotWorkerProxy}
*/
this._workerProxy = null;
/**
* @type {?WebInspector.OutputStream}
*/
this._receiver = null;
/**
* @type {?WebInspector.HeapSnapshotProxy}
*/
this._snapshotProxy = null;
/**
* @type {!Promise.<!WebInspector.HeapSnapshotProxy>}
*/
this._loadPromise = new Promise(loadResolver.bind(this));
this._totalNumberOfChunks = 0;
this._bufferedWriter = null;
/**
* @param {function(!WebInspector.HeapSnapshotProxy)} fulfill
* @this {WebInspector.HeapProfileHeader}
*/
function loadResolver(fulfill)
{
this._fulfillLoad = fulfill;
}
}
WebInspector.HeapProfileHeader.prototype = {
/**
* @override
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @return {!WebInspector.ProfileSidebarTreeElement}
*/
createSidebarTreeElement: function(dataDisplayDelegate)
{
return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
},
/**
* @override
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @return {!WebInspector.HeapSnapshotView}
*/
createView: function(dataDisplayDelegate)
{
return new WebInspector.HeapS