blob: bd7085e3b3bc51d3ecfe164609c9fdde63cd48f3 [file] [log] [blame]
/*
* Copyright (C) 2008 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.Object}
* @param {string} id
* @param {string} name
* @suppressGlobalPropertiesCheck
*/
WebInspector.ProfileType = function(id, name)
{
WebInspector.Object.call(this);
this._id = id;
this._name = name;
/** @type {!Array.<!WebInspector.ProfileHeader>} */
this._profiles = [];
/** @type {?WebInspector.ProfileHeader} */
this._profileBeingRecorded = null;
this._nextProfileUid = 1;
if (!window.opener)
window.addEventListener("unload", this._clearTempStorage.bind(this), false);
}
/**
* @enum {string}
*/
WebInspector.ProfileType.Events = {
AddProfileHeader: "add-profile-header",
ProfileComplete: "profile-complete",
RemoveProfileHeader: "remove-profile-header",
ViewUpdated: "view-updated"
}
WebInspector.ProfileType.prototype = {
/**
* @return {number}
*/
nextProfileUid: function()
{
return this._nextProfileUid;
},
/**
* @return {boolean}
*/
hasTemporaryView: function()
{
return false;
},
/**
* @return {?string}
*/
fileExtension: function()
{
return null;
},
get statusBarItems()
{
return [];
},
get buttonTooltip()
{
return "";
},
get id()
{
return this._id;
},
get treeItemTitle()
{
return this._name;
},
get name()
{
return this._name;
},
/**
* @return {boolean}
*/
buttonClicked: function()
{
return false;
},
get description()
{
return "";
},
/**
* @return {boolean}
*/
isInstantProfile: function()
{
return false;
},
/**
* @return {boolean}
*/
isEnabled: function()
{
return true;
},
/**
* @return {!Array.<!WebInspector.ProfileHeader>}
*/
getProfiles: function()
{
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {boolean}
* @this {WebInspector.ProfileType}
*/
function isFinished(profile)
{
return this._profileBeingRecorded !== profile;
}
return this._profiles.filter(isFinished.bind(this));
},
/**
* @return {?Element}
*/
decorationElement: function()
{
return null;
},
/**
* @nosideeffects
* @param {number} uid
* @return {?WebInspector.ProfileHeader}
*/
getProfile: function(uid)
{
for (var i = 0; i < this._profiles.length; ++i) {
if (this._profiles[i].uid === uid)
return this._profiles[i];
}
return null;
},
/**
* @param {!File} file
*/
loadFromFile: function(file)
{
var name = file.name;
if (name.endsWith(this.fileExtension()))
name = name.substr(0, name.length - this.fileExtension().length);
var profile = this.createProfileLoadedFromFile(name);
profile.setFromFile();
this.setProfileBeingRecorded(profile);
this.addProfile(profile);
profile.loadFromFile(file);
},
/**
* @param {string} title
* @return {!WebInspector.ProfileHeader}
*/
createProfileLoadedFromFile: function(title)
{
throw new Error("Needs implemented.");
},
/**
* @param {!WebInspector.ProfileHeader} profile
*/
addProfile: function(profile)
{
this._profiles.push(profile);
this.dispatchEventToListeners(WebInspector.ProfileType.Events.AddProfileHeader, profile);
},
/**
* @param {!WebInspector.ProfileHeader} profile
*/
removeProfile: function(profile)
{
var index = this._profiles.indexOf(profile);
if (index === -1)
return;
this._profiles.splice(index, 1);
this._disposeProfile(profile);
},
_clearTempStorage: function()
{
for (var i = 0; i < this._profiles.length; ++i)
this._profiles[i].removeTempFile();
},
/**
* @return {?WebInspector.ProfileHeader}
*/
profileBeingRecorded: function()
{
return this._profileBeingRecorded;
},
/**
* @param {?WebInspector.ProfileHeader} profile
*/
setProfileBeingRecorded: function(profile)
{
if (this._profileBeingRecorded && this._profileBeingRecorded.target())
WebInspector.targetManager.resumeAllTargets();
if (profile && profile.target())
WebInspector.targetManager.suspendAllTargets();
this._profileBeingRecorded = profile;
},
profileBeingRecordedRemoved: function()
{
},
_reset: function()
{
var profiles = this._profiles.slice(0);
for (var i = 0; i < profiles.length; ++i)
this._disposeProfile(profiles[i]);
this._profiles = [];
this._nextProfileUid = 1;
},
/**
* @param {!WebInspector.ProfileHeader} profile
*/
_disposeProfile: function(profile)
{
this.dispatchEventToListeners(WebInspector.ProfileType.Events.RemoveProfileHeader, profile);
profile.dispose();
if (this._profileBeingRecorded === profile) {
this.profileBeingRecordedRemoved();
this.setProfileBeingRecorded(null);
}
},
__proto__: WebInspector.Object.prototype
}
/**
* @interface
*/
WebInspector.ProfileType.DataDisplayDelegate = function()
{
}
WebInspector.ProfileType.DataDisplayDelegate.prototype = {
/**
* @param {?WebInspector.ProfileHeader} profile
* @return {?WebInspector.View}
*/
showProfile: function(profile) { },
/**
* @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
* @param {string} perspectiveName
*/
showObject: function(snapshotObjectId, perspectiveName) { }
}
/**
* @constructor
* @extends {WebInspector.Object}
* @param {?WebInspector.Target} target
* @param {!WebInspector.ProfileType} profileType
* @param {string} title
*/
WebInspector.ProfileHeader = function(target, profileType, title)
{
this._target = target;
this._profileType = profileType;
this.title = title;
this.uid = profileType._nextProfileUid++;
this._fromFile = false;
}
/**
* @constructor
* @param {?string} subtitle
* @param {boolean|undefined} wait
*/
WebInspector.ProfileHeader.StatusUpdate = function(subtitle, wait)
{
/** @type {?string} */
this.subtitle = subtitle;
/** @type {boolean|undefined} */
this.wait = wait;
}
WebInspector.ProfileHeader.Events = {
UpdateStatus: "UpdateStatus",
ProfileReceived: "ProfileReceived"
}
WebInspector.ProfileHeader.prototype = {
/**
* @return {?WebInspector.Target}
*/
target: function()
{
return this._target;
},
/**
* @return {!WebInspector.ProfileType}
*/
profileType: function()
{
return this._profileType;
},
/**
* @param {?string} subtitle
* @param {boolean=} wait
*/
updateStatus: function(subtitle, wait)
{
this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.UpdateStatus, new WebInspector.ProfileHeader.StatusUpdate(subtitle, wait));
},
/**
* Must be implemented by subclasses.
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @return {!WebInspector.ProfileSidebarTreeElement}
*/
createSidebarTreeElement: function(dataDisplayDelegate)
{
throw new Error("Needs implemented.");
},
/**
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @return {!WebInspector.View}
*/
createView: function(dataDisplayDelegate)
{
throw new Error("Not implemented.");
},
removeTempFile: function()
{
if (this._tempFile)
this._tempFile.remove();
},
dispose: function()
{
},
/**
* @param {!Function} callback
*/
load: function(callback)
{
},
/**
* @return {boolean}
*/
canSaveToFile: function()
{
return false;
},
saveToFile: function()
{
throw new Error("Needs implemented");
},
/**
* @param {!File} file
*/
loadFromFile: function(file)
{
throw new Error("Needs implemented");
},
/**
* @return {boolean}
*/
fromFile: function()
{
return this._fromFile;
},
setFromFile: function()
{
this._fromFile = true;
},
__proto__: WebInspector.Object.prototype
}
/**
* @constructor
* @implements {WebInspector.Searchable}
* @implements {WebInspector.ProfileType.DataDisplayDelegate}
* @extends {WebInspector.PanelWithSidebarTree}
*/
WebInspector.ProfilesPanel = function()
{
WebInspector.PanelWithSidebarTree.call(this, "profiles");
this.registerRequiredCSS("components/panelEnablerView.css");
this.registerRequiredCSS("profiler/heapProfiler.css");
this.registerRequiredCSS("profiler/profilesPanel.css");
this._searchableView = new WebInspector.SearchableView(this);
var mainView = new WebInspector.VBox();
this._searchableView.show(mainView.element);
mainView.show(this.mainElement());
this.profilesItemTreeElement = new WebInspector.ProfilesSidebarTreeElement(this);
this.sidebarTree.appendChild(this.profilesItemTreeElement);
this.profileViews = createElement("div");
this.profileViews.id = "profile-views";
this.profileViews.classList.add("vbox");
this._searchableView.element.appendChild(this.profileViews);
var statusBarContainer = createElementWithClass("div", "profiles-status-bar");
mainView.element.insertBefore(statusBarContainer, mainView.element.firstChild);
this._statusBarElement = statusBarContainer.createChild("div", "status-bar");
this.sidebarElement().classList.add("profiles-sidebar-tree-box");
var statusBarContainerLeft = createElementWithClass("div", "profiles-status-bar");
this.sidebarElement().insertBefore(statusBarContainerLeft, this.sidebarElement().firstChild);
this._statusBarButtons = statusBarContainerLeft.createChild("div", "status-bar");
this.recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
this.recordButton.addEventListener("click", this.toggleRecordButton, this);
this._statusBarButtons.appendChild(this.recordButton.element);
this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");
this.clearResultsButton.addEventListener("click", this._reset, this);
this._statusBarButtons.appendChild(this.clearResultsButton.element);
this._profileTypeStatusBarItemsContainer = this._statusBarElement.createChild("div");
this._profileViewStatusBarItemsContainer = this._statusBarElement.createChild("div");
this._profileGroups = {};
this._launcherView = new WebInspector.MultiProfileLauncherView(this);
this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);
this._profileToView = [];
this._typeIdToSidebarSection = {};
var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
for (var i = 0; i < types.length; i++)
this._registerProfileType(types[i]);
this._launcherView.restoreSelectedProfileType();
this.profilesItemTreeElement.select();
this._showLauncherView();
this._createFileSelectorElement();
this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
this._registerShortcuts();
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged, this);
}
WebInspector.ProfilesPanel.prototype = {
/**
* @return {!WebInspector.SearchableView}
*/
searchableView: function()
{
return this._searchableView;
},
_createFileSelectorElement: function()
{
if (this._fileSelectorElement)
this.element.removeChild(this._fileSelectorElement);
this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
this.element.appendChild(this._fileSelectorElement);
},
_findProfileTypeByExtension: function(fileName)
{
var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
for (var i = 0; i < types.length; i++) {
var type = types[i];
var extension = type.fileExtension();
if (!extension)
continue;
if (fileName.endsWith(type.fileExtension()))
return type;
}
return null;
},
_registerShortcuts: function()
{
this.registerShortcuts(WebInspector.ShortcutsScreen.ProfilesPanelShortcuts.StartStopRecording, this.toggleRecordButton.bind(this));
},
/**
* @param {!File} file
*/
_loadFromFile: function(file)
{
this._createFileSelectorElement();
var profileType = this._findProfileTypeByExtension(file.name);
if (!profileType) {
var extensions = [];
var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
for (var i = 0; i < types.length; i++) {
var extension = types[i].fileExtension();
if (!extension || extensions.indexOf(extension) !== -1)
continue;
extensions.push(extension);
}
WebInspector.console.error(WebInspector.UIString("Can't load file. Only files with extensions '%s' can be loaded.", extensions.join("', '")));
return;
}
if (!!profileType.profileBeingRecorded()) {
WebInspector.console.error(WebInspector.UIString("Can't load profile while another profile is recording."));
return;
}
profileType.loadFromFile(file);
},
/**
* @return {boolean}
*/
toggleRecordButton: function()
{
if (!this.recordButton.enabled())
return true;
var type = this._selectedProfileType;
var isProfiling = type.buttonClicked();
this._updateRecordButton(isProfiling);
if (isProfiling) {
this._launcherView.profileStarted();
if (type.hasTemporaryView())
this.showProfile(type.profileBeingRecorded());
} else {
this._launcherView.profileFinished();
}
return true;
},
_onSuspendStateChanged: function()
{
this._updateRecordButton(this.recordButton.toggled);
},
/**
* @param {boolean} toggled
*/
_updateRecordButton: function(toggled)
{
var enable = toggled || !WebInspector.targetManager.allTargetsSuspended();
this.recordButton.setEnabled(enable);
this.recordButton.toggled = toggled;
if (enable)
this.recordButton.title = this._selectedProfileType ? this._selectedProfileType.buttonTooltip : "";
else
this.recordButton.title = WebInspector.anotherProfilerActiveLabel();
if (this._selectedProfileType)
this._launcherView.updateProfileType(this._selectedProfileType, enable);
},
_profileBeingRecordedRemoved: function()
{
this._updateRecordButton(false);
this._launcherView.profileFinished();
},
/**
* @param {!WebInspector.Event} event
*/
_onProfileTypeSelected: function(event)
{
this._selectedProfileType = /** @type {!WebInspector.ProfileType} */ (event.data);
this._updateProfileTypeSpecificUI();
},
_updateProfileTypeSpecificUI: function()
{
this._updateRecordButton(this.recordButton.toggled);
this._profileTypeStatusBarItemsContainer.removeChildren();
var statusBarItems = this._selectedProfileType.statusBarItems;
if (statusBarItems) {
for (var i = 0; i < statusBarItems.length; ++i)
this._profileTypeStatusBarItemsContainer.appendChild(statusBarItems[i]);
}
},
_reset: function()
{
WebInspector.Panel.prototype.reset.call(this);
var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
for (var i = 0; i < types.length; i++)
types[i]._reset();
delete this.visibleView;
delete this.currentQuery;
this.searchCanceled();
this._profileGroups = {};
this._updateRecordButton(false);
this._launcherView.profileFinished();
this.sidebarTree.element.classList.remove("some-expandable");
this._launcherView.detach();
this.profileViews.removeChildren();
this._profileViewStatusBarItemsContainer.removeChildren();
this.removeAllListeners();
this.recordButton.visible = true;
this._profileViewStatusBarItemsContainer.classList.remove("hidden");
this.clearResultsButton.element.classList.remove("hidden");
this.profilesItemTreeElement.select();
this._showLauncherView();
},
_showLauncherView: function()
{
this.closeVisibleView();
this._profileViewStatusBarItemsContainer.removeChildren();
this._launcherView.show(this.profileViews);
this.visibleView = this._launcherView;
},
/**
* @param {!WebInspector.ProfileType} profileType
*/
_registerProfileType: function(profileType)
{
this._launcherView.addProfileType(profileType);
var profileTypeSection = new WebInspector.ProfileTypeSidebarSection(this, profileType);
this._typeIdToSidebarSection[profileType.id] = profileTypeSection
this.sidebarTree.appendChild(profileTypeSection);
profileTypeSection.childrenListElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
/**
* @param {!WebInspector.Event} event
* @this {WebInspector.ProfilesPanel}
*/
function onAddProfileHeader(event)
{
this._addProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
}
/**
* @param {!WebInspector.Event} event
* @this {WebInspector.ProfilesPanel}
*/
function onRemoveProfileHeader(event)
{
this._removeProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
}
/**
* @param {!WebInspector.Event} event
* @this {WebInspector.ProfilesPanel}
*/
function profileComplete(event)
{
this.showProfile(/** @type {!WebInspector.ProfileHeader} */ (event.data));
}
profileType.addEventListener(WebInspector.ProfileType.Events.ViewUpdated, this._updateProfileTypeSpecificUI, this);
profileType.addEventListener(WebInspector.ProfileType.Events.AddProfileHeader, onAddProfileHeader, this);
profileType.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, onRemoveProfileHeader, this);
profileType.addEventListener(WebInspector.ProfileType.Events.ProfileComplete, profileComplete, this);
var profiles = profileType.getProfiles();
for (var i = 0; i < profiles.length; i++)
this._addProfileHeader(profiles[i]);
},
/**
* @param {!Event} event
*/
_handleContextMenuEvent: function(event)
{
var element = event.srcElement;
while (element && !element.treeElement && element !== this.element)
element = element.parentElement;
if (!element)
return;
if (element.treeElement && element.treeElement.handleContextMenuEvent) {
element.treeElement.handleContextMenuEvent(event, this);
return;
}
var contextMenu = new WebInspector.ContextMenu(event);
if (this.visibleView instanceof WebInspector.HeapSnapshotView) {
this.visibleView.populateContextMenu(contextMenu, event);
}
if (element !== this.element || event.srcElement === this.sidebarElement()) {
contextMenu.appendItem(WebInspector.UIString("Load\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
}
contextMenu.show();
},
showLoadFromFileDialog: function()
{
this._fileSelectorElement.click();
},
/**
* @param {!WebInspector.ProfileHeader} profile
*/
_addProfileHeader: function(profile)
{
var profileType = profile.profileType();
var typeId = profileType.id;
this._typeIdToSidebarSection[typeId].addProfileHeader(profile);
if (!this.visibleView || this.visibleView === this._launcherView)
this.showProfile(profile);
},
/**
* @param {!WebInspector.ProfileHeader} profile
*/
_removeProfileHeader: function(profile)
{
if (profile.profileType()._profileBeingRecorded === profile)
this._profileBeingRecordedRemoved();
var i = this._indexOfViewForProfile(profile);
if (i !== -1)
this._profileToView.splice(i, 1);
var profileType = profile.profileType();
var typeId = profileType.id;
var sectionIsEmpty = this._typeIdToSidebarSection[typeId].removeProfileHeader(profile);
// No other item will be selected if there aren't any other profiles, so
// make sure that view gets cleared when the last profile is removed.
if (sectionIsEmpty) {
this.profilesItemTreeElement.select();
this._showLauncherView();
}
},
/**
* @param {?WebInspector.ProfileHeader} profile
* @return {?WebInspector.View}
*/
showProfile: function(profile)
{
if (!profile || (profile.profileType().profileBeingRecorded() === profile) && !profile.profileType().hasTemporaryView())
return null;
var view = this._viewForProfile(profile);
if (view === this.visibleView)
return view;
this.closeVisibleView();
view.show(this.profileViews);
view.focus();
this.visibleView = view;
var profileTypeSection = this._typeIdToSidebarSection[profile.profileType().id];
var sidebarElement = profileTypeSection.sidebarElementForProfile(profile);
sidebarElement.revealAndSelect();
this._profileViewStatusBarItemsContainer.removeChildren();
var statusBarItems = view.statusBarItems;
if (statusBarItems)
for (var i = 0; i < statusBarItems.length; ++i)
this._profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
return view;
},
/**
* @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
* @param {string} perspectiveName
*/
showObject: function(snapshotObjectId, perspectiveName)
{
var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
for (var i = 0; i < heapProfiles.length; i++) {
var profile = heapProfiles[i];
// FIXME: allow to choose snapshot if there are several options.
if (profile.maxJSObjectId >= snapshotObjectId) {
this.showProfile(profile);
var view = this._viewForProfile(profile);
view.highlightLiveObject(perspectiveName, snapshotObjectId);
break;
}
}
},
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {!WebInspector.View}
*/
_viewForProfile: function(profile)
{
var index = this._indexOfViewForProfile(profile);
if (index !== -1)
return this._profileToView[index].view;
var view = profile.createView(this);
view.element.classList.add("profile-view");
this._profileToView.push({ profile: profile, view: view});
return view;
},
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {number}
*/
_indexOfViewForProfile: function(profile)
{
for (var i = 0; i < this._profileToView.length; i++) {
if (this._profileToView[i].profile === profile)
return i;
}
return -1;
},
closeVisibleView: function()
{
if (this.visibleView)
this.visibleView.detach();
delete this.visibleView;
},
/**
* @param {!WebInspector.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch: function(searchConfig, shouldJump, jumpBackwards)
{
var query = searchConfig.query;
this.searchCanceled();
var visibleView = this.visibleView;
if (!visibleView)
return;
/**
* @this {WebInspector.ProfilesPanel}
*/
function finishedCallback(view, searchMatches)
{
if (!searchMatches)
return;
this._searchableView.updateSearchMatchesCount(searchMatches);
this._searchResultsView = view;
if (shouldJump) {
if (jumpBackwards)
view.jumpToLastSearchResult();
else
view.jumpToFirstSearchResult();
this._searchableView.updateCurrentMatchIndex(view.currentSearchResultIndex());
}
}
visibleView.currentQuery = query;
visibleView.performSearch(query, finishedCallback.bind(this));
},
jumpToNextSearchResult: function()
{
if (!this._searchResultsView)
return;
if (this._searchResultsView !== this.visibleView)
return;
this._searchResultsView.jumpToNextSearchResult();
this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
},
jumpToPreviousSearchResult: function()
{
if (!this._searchResultsView)
return;
if (this._searchResultsView !== this.visibleView)
return;
this._searchResultsView.jumpToPreviousSearchResult();
this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
},
/**
* @return {boolean}
*/
supportsCaseSensitiveSearch: function()
{
return false;
},
/**
* @return {boolean}
*/
supportsRegexSearch: function()
{
return false;
},
searchCanceled: function()
{
if (this._searchResultsView) {
if (this._searchResultsView.searchCanceled)
this._searchResultsView.searchCanceled();
this._searchResultsView.currentQuery = null;
this._searchResultsView = null;
}
this._searchableView.updateSearchMatchesCount(0);
},
/**
* @param {!Event} event
* @param {!WebInspector.ContextMenu} contextMenu
* @param {!Object} target
*/
appendApplicableItems: function(event, contextMenu, target)
{
if (!(target instanceof WebInspector.RemoteObject))
return;
if (WebInspector.inspectorView.currentPanel() !== this)
return;
var object = /** @type {!WebInspector.RemoteObject} */ (target);
var objectId = object.objectId;
if (!objectId)
return;
var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
if (!heapProfiles.length)
return;
/**
* @this {WebInspector.ProfilesPanel}
*/
function revealInView(viewName)
{
object.target().heapProfilerAgent().getHeapObjectId(objectId, didReceiveHeapObjectId.bind(this, viewName));
}
/**
* @this {WebInspector.ProfilesPanel}
*/
function didReceiveHeapObjectId(viewName, error, result)
{
if (WebInspector.inspectorView.currentPanel() !== this)
return;
if (!error)
this.showObject(result, viewName);
}
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInView.bind(this, "Summary"));
},
__proto__: WebInspector.PanelWithSidebarTree.prototype
}
/**
* @constructor
* @extends {WebInspector.SidebarSectionTreeElement}
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @param {!WebInspector.ProfileType} profileType
*/
WebInspector.ProfileTypeSidebarSection = function(dataDisplayDelegate, profileType)
{
WebInspector.SidebarSectionTreeElement.call(this, profileType.treeItemTitle, null, true);
this._dataDisplayDelegate = dataDisplayDelegate;
this._profileTreeElements = [];
this._profileGroups = {};
this.hidden = true;
}
/**
* @constructor
*/
WebInspector.ProfileTypeSidebarSection.ProfileGroup = function()
{
this.profileSidebarTreeElements = [];
this.sidebarTreeElement = null;
}
WebInspector.ProfileTypeSidebarSection.prototype = {
/**
* @param {!WebInspector.ProfileHeader} profile
*/
addProfileHeader: function(profile)
{
this.hidden = false;
var profileType = profile.profileType();
var sidebarParent = this;
var profileTreeElement = profile.createSidebarTreeElement(this._dataDisplayDelegate);
this._profileTreeElements.push(profileTreeElement);
if (!profile.fromFile() && profileType.profileBeingRecorded() !== profile) {
var profileTitle = profile.title;
var group = this._profileGroups[profileTitle];
if (!group) {
group = new WebInspector.ProfileTypeSidebarSection.ProfileGroup();
this._profileGroups[profileTitle] = group;
}
group.profileSidebarTreeElements.push(profileTreeElement);
var groupSize = group.profileSidebarTreeElements.length;
if (groupSize === 2) {
// Make a group TreeElement now that there are 2 profiles.
group.sidebarTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(this._dataDisplayDelegate, profile.title);
var firstProfileTreeElement = group.profileSidebarTreeElements[0];
// Insert at the same index for the first profile of the group.
var index = this.children.indexOf(firstProfileTreeElement);
this.insertChild(group.sidebarTreeElement, index);
// Move the first profile to the group.
var selected = firstProfileTreeElement.selected;
this.removeChild(firstProfileTreeElement);
group.sidebarTreeElement.appendChild(firstProfileTreeElement);
if (selected)
firstProfileTreeElement.revealAndSelect();
firstProfileTreeElement.small = true;
firstProfileTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
this.treeOutline.element.classList.add("some-expandable");
}
if (groupSize >= 2) {
sidebarParent = group.sidebarTreeElement;
profileTreeElement.small = true;
profileTreeElement.mainTitle = WebInspector.UIString("Run %d", groupSize);
}
}
sidebarParent.appendChild(profileTreeElement);
},
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {boolean}
*/
removeProfileHeader: function(profile)
{
var index = this._sidebarElementIndex(profile);
if (index === -1)
return false;
var profileTreeElement = this._profileTreeElements[index];
this._profileTreeElements.splice(index, 1);
var sidebarParent = this;
var group = this._profileGroups[profile.title];
if (group) {
var groupElements = group.profileSidebarTreeElements;
groupElements.splice(groupElements.indexOf(profileTreeElement), 1);
if (groupElements.length === 1) {
// Move the last profile out of its group and remove the group.
var pos = sidebarParent.children.indexOf(group.sidebarTreeElement);
this.insertChild(groupElements[0], pos);
groupElements[0].small = false;
groupElements[0].mainTitle = group.sidebarTreeElement.title;
this.removeChild(group.sidebarTreeElement);
}
if (groupElements.length !== 0)
sidebarParent = group.sidebarTreeElement;
}
sidebarParent.removeChild(profileTreeElement);
profileTreeElement.dispose();
if (this.children.length)
return false;
this.hidden = true;
return true;
},
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {?WebInspector.ProfileSidebarTreeElement}
*/
sidebarElementForProfile: function(profile)
{
var index = this._sidebarElementIndex(profile);
return index === -1 ? null : this._profileTreeElements[index];
},
/**
* @param {!WebInspector.ProfileHeader} profile
* @return {number}
*/
_sidebarElementIndex: function(profile)
{
var elements = this._profileTreeElements;
for (var i = 0; i < elements.length; i++) {
if (elements[i].profile === profile)
return i;
}
return -1;
},
__proto__: WebInspector.SidebarSectionTreeElement.prototype
}
/**
* @constructor
* @implements {WebInspector.ContextMenu.Provider}
*/
WebInspector.ProfilesPanel.ContextMenuProvider = function()
{
}
WebInspector.ProfilesPanel.ContextMenuProvider.prototype = {
/**
* @param {!Event} event
* @param {!WebInspector.ContextMenu} contextMenu
* @param {!Object} target
*/
appendApplicableItems: function(event, contextMenu, target)
{
WebInspector.ProfilesPanel._instance().appendApplicableItems(event, contextMenu, target);
}
}
/**
* @constructor
* @extends {WebInspector.SidebarTreeElement}
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @param {!WebInspector.ProfileHeader} profile
* @param {string} className
*/
WebInspector.ProfileSidebarTreeElement = function(dataDisplayDelegate, profile, className)
{
this._dataDisplayDelegate = dataDisplayDelegate;
this.profile = profile;
WebInspector.SidebarTreeElement.call(this, className, profile.title, "", profile, false);
this.refreshTitles();
profile.addEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
if (profile.canSaveToFile())
this._createSaveLink();
else
profile.addEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
}
WebInspector.ProfileSidebarTreeElement.prototype = {
_createSaveLink: function()
{
this._saveLinkElement = this.titleContainer.createChild("span", "save-link");
this._saveLinkElement.textContent = WebInspector.UIString("Save");
this._saveLinkElement.addEventListener("click", this._saveProfile.bind(this), false);
},
_onProfileReceived: function(event)
{
this._createSaveLink();
},
/**
* @param {!WebInspector.Event} event
*/
_updateStatus: function(event)
{
var statusUpdate = event.data;
if (statusUpdate.subtitle !== null)
this.subtitle = statusUpdate.subtitle;
if (typeof statusUpdate.wait === "boolean")
this.wait = statusUpdate.wait;
this.refreshTitles();
},
dispose: function()
{
this.profile.removeEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
this.profile.removeEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
},
/**
* @return {boolean}
*/
onselect: function()
{
this._dataDisplayDelegate.showProfile(this.profile);
return true;
},
/**
* @return {boolean}
*/
ondelete: function()
{
this.profile.profileType().removeProfile(this.profile);
return true;
},
/**
* @param {!Event} event
* @param {!WebInspector.ProfilesPanel} panel
*/
handleContextMenuEvent: function(event, panel)
{
var profile = this.profile;
var contextMenu = new WebInspector.ContextMenu(event);
// FIXME: use context menu provider
contextMenu.appendItem(WebInspector.UIString("Load\u2026"), panel._fileSelectorElement.click.bind(panel._fileSelectorElement));
if (profile.canSaveToFile())
contextMenu.appendItem(WebInspector.UIString("Save\u2026"), profile.saveToFile.bind(profile));
contextMenu.appendItem(WebInspector.UIString("Delete"), this.ondelete.bind(this));
contextMenu.show();
},
_saveProfile: function(event)
{
this.profile.saveToFile();
},
__proto__: WebInspector.SidebarTreeElement.prototype
}
/**
* @constructor
* @extends {WebInspector.SidebarTreeElement}
* @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
* @param {string} title
* @param {string=} subtitle
*/
WebInspector.ProfileGroupSidebarTreeElement = function(dataDisplayDelegate, title, subtitle)
{
WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
this._dataDisplayDelegate = dataDisplayDelegate;
}
WebInspector.ProfileGroupSidebarTreeElement.prototype = {
/**
* @return {boolean}
*/
onselect: function()
{
var hasChildren = this.children.length > 0;
if (hasChildren)
this._dataDisplayDelegate.showProfile(this.children[this.children.length - 1].profile);
return hasChildren;
},
__proto__: WebInspector.SidebarTreeElement.prototype
}
/**
* @constructor
* @extends {WebInspector.SidebarTreeElement}
* @param {!WebInspector.ProfilesPanel} panel
*/
WebInspector.ProfilesSidebarTreeElement = function(panel)
{
this._panel = panel;
this.small = false;
WebInspector.SidebarTreeElement.call(this, "profile-launcher-view-tree-item", WebInspector.UIString("Profiles"), "", null, false);
}
WebInspector.ProfilesSidebarTreeElement.prototype = {
/**
* @return {boolean}
*/
onselect: function()
{
this._panel._showLauncherView();
return true;
},
get selectable()
{
return true;
},
__proto__: WebInspector.SidebarTreeElement.prototype
}
WebInspector.ProfilesPanel.show = function()
{
WebInspector.inspectorView.setCurrentPanel(WebInspector.ProfilesPanel._instance());
}
/**
* @return {!WebInspector.ProfilesPanel}
*/
WebInspector.ProfilesPanel._instance = function()
{
if (!WebInspector.ProfilesPanel._instanceObject)
WebInspector.ProfilesPanel._instanceObject = new WebInspector.ProfilesPanel();
return WebInspector.ProfilesPanel._instanceObject;
}
/**
* @constructor
* @implements {WebInspector.PanelFactory}
*/
WebInspector.ProfilesPanelFactory = function()
{
}
WebInspector.ProfilesPanelFactory.prototype = {
/**
* @return {!WebInspector.Panel}
*/
createPanel: function()
{
return WebInspector.ProfilesPanel._instance();
}
}