| /* |
| * 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. |
| */ |
| |
| const UserInitiatedProfileName = "org.webkit.profiles.user-initiated"; |
| |
| WebInspector.ProfileType = function(id, name) |
| { |
| this._id = id; |
| this._name = name; |
| } |
| |
| WebInspector.ProfileType.URLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/; |
| |
| WebInspector.ProfileType.prototype = { |
| get buttonTooltip() |
| { |
| return ""; |
| }, |
| |
| get buttonStyle() |
| { |
| return undefined; |
| }, |
| |
| get buttonCaption() |
| { |
| return this.name; |
| }, |
| |
| get id() |
| { |
| return this._id; |
| }, |
| |
| get name() |
| { |
| return this._name; |
| }, |
| |
| buttonClicked: function() |
| { |
| }, |
| |
| viewForProfile: function(profile) |
| { |
| if (!profile._profileView) |
| profile._profileView = this.createView(profile); |
| return profile._profileView; |
| }, |
| |
| get welcomeMessage() |
| { |
| return ""; |
| }, |
| |
| // Must be implemented by subclasses. |
| createView: function(profile) |
| { |
| throw new Error("Needs implemented."); |
| }, |
| |
| // Must be implemented by subclasses. |
| createSidebarTreeElementForProfile: function(profile) |
| { |
| throw new Error("Needs implemented."); |
| } |
| } |
| |
| WebInspector.ProfilesPanel = function() |
| { |
| WebInspector.Panel.call(this, "profiles"); |
| |
| this.createSidebar(); |
| |
| this._profileTypesByIdMap = {}; |
| this._profileTypeButtonsByIdMap = {}; |
| |
| var panelEnablerHeading = WebInspector.UIString("You need to enable profiling before you can use the Profiles panel."); |
| var panelEnablerDisclaimer = WebInspector.UIString("Enabling profiling will make scripts run slower."); |
| var panelEnablerButton = WebInspector.UIString("Enable Profiling"); |
| this.panelEnablerView = new WebInspector.PanelEnablerView("profiles", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); |
| this.panelEnablerView.addEventListener("enable clicked", this._enableProfiling, this); |
| |
| this.element.appendChild(this.panelEnablerView.element); |
| |
| this.profileViews = document.createElement("div"); |
| this.profileViews.id = "profile-views"; |
| this.element.appendChild(this.profileViews); |
| |
| this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); |
| this.enableToggleButton.addEventListener("click", this._toggleProfiling.bind(this), false); |
| |
| this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item"); |
| this.clearResultsButton.addEventListener("click", this._clearProfiles.bind(this), false); |
| |
| this.profileViewStatusBarItemsContainer = document.createElement("div"); |
| this.profileViewStatusBarItemsContainer.className = "status-bar-items"; |
| |
| this.welcomeView = new WebInspector.WelcomeView("profiles", WebInspector.UIString("Welcome to the Profiles panel")); |
| this.element.appendChild(this.welcomeView.element); |
| |
| this._profiles = []; |
| this._profilerEnabled = Preferences.profilerAlwaysEnabled; |
| this._reset(); |
| |
| this._registerProfileType(new WebInspector.CPUProfileType()); |
| if (Preferences.heapProfilerPresent) { |
| if (!Preferences.detailedHeapProfiles) |
| this._registerProfileType(new WebInspector.HeapSnapshotProfileType()); |
| else |
| this._registerProfileType(new WebInspector.DetailedHeapshotProfileType()); |
| } |
| |
| InspectorBackend.registerDomainDispatcher("Profiler", new WebInspector.ProfilerDispatcher(this)); |
| |
| if (Preferences.profilerAlwaysEnabled || WebInspector.settings.profilerEnabled) |
| ProfilerAgent.enable(); |
| else { |
| function onProfilerEnebled(error, value) { |
| if (value) |
| this._profilerWasEnabled(); |
| } |
| ProfilerAgent.isEnabled(onProfilerEnebled.bind(this)); |
| } |
| } |
| |
| WebInspector.ProfilesPanel.prototype = { |
| get toolbarItemLabel() |
| { |
| return WebInspector.UIString("Profiles"); |
| }, |
| |
| get statusBarItems() |
| { |
| function clickHandler(profileType, buttonElement) |
| { |
| profileType.buttonClicked.call(profileType); |
| this.updateProfileTypeButtons(); |
| } |
| |
| var items = [this.enableToggleButton.element]; |
| // FIXME: Generate a single "combo-button". |
| for (var typeId in this._profileTypesByIdMap) { |
| var profileType = this.getProfileType(typeId); |
| if (profileType.buttonStyle) { |
| var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption); |
| this._profileTypeButtonsByIdMap[typeId] = button.element; |
| button.element.addEventListener("click", clickHandler.bind(this, profileType, button.element), false); |
| items.push(button.element); |
| } |
| } |
| items.push(this.clearResultsButton.element, this.profileViewStatusBarItemsContainer); |
| return items; |
| }, |
| |
| show: function() |
| { |
| WebInspector.Panel.prototype.show.call(this); |
| this._populateProfiles(); |
| }, |
| |
| _profilerWasEnabled: function() |
| { |
| if (this._profilerEnabled) |
| return; |
| |
| this._profilerEnabled = true; |
| |
| this._reset(); |
| if (this.visible) |
| this._populateProfiles(); |
| }, |
| |
| _profilerWasDisabled: function() |
| { |
| if (!this._profilerEnabled) |
| return; |
| |
| this._profilerEnabled = false; |
| this._reset(); |
| }, |
| |
| _reset: function() |
| { |
| WebInspector.Panel.prototype.reset.call(this); |
| |
| for (var i = 0; i < this._profiles.length; ++i) { |
| var view = this._profiles[i]._profileView; |
| if (view && ("dispose" in view)) |
| view.dispose(); |
| delete this._profiles[i]._profileView; |
| } |
| delete this.visibleView; |
| |
| delete this.currentQuery; |
| this.searchCanceled(); |
| |
| this._profiles = []; |
| this._profilesIdMap = {}; |
| this._profileGroups = {}; |
| this._profileGroupsForLinks = {}; |
| this._profilesWereRequested = false; |
| |
| this.sidebarTreeElement.removeStyleClass("some-expandable"); |
| |
| for (var typeId in this._profileTypesByIdMap) |
| this.getProfileType(typeId).treeElement.removeChildren(); |
| |
| this.profileViews.removeChildren(); |
| |
| this.profileViewStatusBarItemsContainer.removeChildren(); |
| |
| this.removeAllListeners(); |
| |
| this._updateInterface(); |
| this.welcomeView.show(); |
| }, |
| |
| _clearProfiles: function() |
| { |
| ProfilerAgent.clearProfiles(); |
| this._reset(); |
| }, |
| |
| _registerProfileType: function(profileType) |
| { |
| this._profileTypesByIdMap[profileType.id] = profileType; |
| profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.name, null, true); |
| this.sidebarTree.appendChild(profileType.treeElement); |
| profileType.treeElement.expand(); |
| this._addWelcomeMessage(profileType); |
| }, |
| |
| _addWelcomeMessage: function(profileType) |
| { |
| var message = profileType.welcomeMessage; |
| // Message text is supposed to have a '%s' substring as a placeholder |
| // for a status bar button. If it is there, we split the message in two |
| // parts, and insert the button between them. |
| var buttonPos = message.indexOf("%s"); |
| if (buttonPos > -1) { |
| var container = document.createDocumentFragment(); |
| var part1 = document.createElement("span"); |
| part1.textContent = message.substr(0, buttonPos); |
| container.appendChild(part1); |
| |
| var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption); |
| container.appendChild(button.element); |
| |
| var part2 = document.createElement("span"); |
| part2.textContent = message.substr(buttonPos + 2); |
| container.appendChild(part2); |
| this.welcomeView.addMessage(container); |
| } else |
| this.welcomeView.addMessage(message); |
| }, |
| |
| _makeKey: function(text, profileTypeId) |
| { |
| return escape(text) + '/' + escape(profileTypeId); |
| }, |
| |
| _addProfileHeader: function(profile) |
| { |
| if (this.hasTemporaryProfile(profile.typeId)) { |
| if (profile.typeId === WebInspector.CPUProfileType.TypeId) |
| this._removeProfileHeader(this._temporaryRecordingProfile); |
| else |
| this._removeProfileHeader(this._temporaryTakingSnapshot); |
| } |
| |
| var typeId = profile.typeId; |
| var profileType = this.getProfileType(typeId); |
| var sidebarParent = profileType.treeElement; |
| var small = false; |
| var alternateTitle; |
| |
| profile.__profilesPanelProfileType = profileType; |
| this._profiles.push(profile); |
| this._profilesIdMap[this._makeKey(profile.uid, typeId)] = profile; |
| |
| if (profile.title.indexOf(UserInitiatedProfileName) !== 0) { |
| var profileTitleKey = this._makeKey(profile.title, typeId); |
| if (!(profileTitleKey in this._profileGroups)) |
| this._profileGroups[profileTitleKey] = []; |
| |
| var group = this._profileGroups[profileTitleKey]; |
| group.push(profile); |
| |
| if (group.length === 2) { |
| // Make a group TreeElement now that there are 2 profiles. |
| group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title); |
| |
| // Insert at the same index for the first profile of the group. |
| var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement); |
| sidebarParent.insertChild(group._profilesTreeElement, index); |
| |
| // Move the first profile to the group. |
| var selected = group[0]._profilesTreeElement.selected; |
| sidebarParent.removeChild(group[0]._profilesTreeElement); |
| group._profilesTreeElement.appendChild(group[0]._profilesTreeElement); |
| if (selected) { |
| group[0]._profilesTreeElement.select(); |
| group[0]._profilesTreeElement.reveal(); |
| } |
| |
| group[0]._profilesTreeElement.small = true; |
| group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1); |
| |
| this.sidebarTreeElement.addStyleClass("some-expandable"); |
| } |
| |
| if (group.length >= 2) { |
| sidebarParent = group._profilesTreeElement; |
| alternateTitle = WebInspector.UIString("Run %d", group.length); |
| small = true; |
| } |
| } |
| |
| var profileTreeElement = profileType.createSidebarTreeElementForProfile(profile); |
| profile.sideBarElement = profileTreeElement; |
| profileTreeElement.small = small; |
| if (alternateTitle) |
| profileTreeElement.mainTitle = alternateTitle; |
| profile._profilesTreeElement = profileTreeElement; |
| |
| sidebarParent.appendChild(profileTreeElement); |
| if (!profile.isTemporary) { |
| this.welcomeView.hide(); |
| if (!this.visibleView) |
| this.showProfile(profile); |
| this.dispatchEventToListeners("profile added"); |
| } |
| }, |
| |
| _removeProfileHeader: function(profile) |
| { |
| var typeId = profile.typeId; |
| var profileType = this.getProfileType(typeId); |
| var sidebarParent = profileType.treeElement; |
| |
| for (var i = 0; i < this._profiles.length; ++i) { |
| if (this._profiles[i].uid === profile.uid) { |
| profile = this._profiles[i]; |
| this._profiles.splice(i, 1); |
| break; |
| } |
| } |
| delete this._profilesIdMap[this._makeKey(profile.uid, typeId)]; |
| |
| var profileTitleKey = this._makeKey(profile.title, typeId); |
| delete this._profileGroups[profileTitleKey]; |
| |
| sidebarParent.removeChild(profile._profilesTreeElement); |
| |
| if (!profile.isTemporary) |
| ProfilerAgent.removeProfile(profile.typeId, profile.uid); |
| |
| // 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 (!this._profiles.length) |
| this.closeVisibleView(); |
| }, |
| |
| showProfile: function(profile) |
| { |
| if (!profile || profile.isTemporary) |
| return; |
| |
| this.closeVisibleView(); |
| |
| var view = profile.__profilesPanelProfileType.viewForProfile(profile); |
| |
| view.show(this.profileViews); |
| |
| profile._profilesTreeElement.select(true); |
| profile._profilesTreeElement.reveal(); |
| |
| this.visibleView = view; |
| |
| this.profileViewStatusBarItemsContainer.removeChildren(); |
| |
| var statusBarItems = view.statusBarItems; |
| if (statusBarItems) |
| for (var i = 0; i < statusBarItems.length; ++i) |
| this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]); |
| }, |
| |
| getProfiles: function(typeId) |
| { |
| var result = []; |
| var profilesCount = this._profiles.length; |
| for (var i = 0; i < profilesCount; ++i) { |
| var profile = this._profiles[i]; |
| if (!profile.isTemporary && profile.typeId === typeId) |
| result.push(profile); |
| } |
| return result; |
| }, |
| |
| hasTemporaryProfile: function(typeId) |
| { |
| var profilesCount = this._profiles.length; |
| for (var i = 0; i < profilesCount; ++i) |
| if (this._profiles[i].typeId === typeId && this._profiles[i].isTemporary) |
| return true; |
| return false; |
| }, |
| |
| hasProfile: function(profile) |
| { |
| return !!this._profilesIdMap[this._makeKey(profile.uid, profile.typeId)]; |
| }, |
| |
| getProfile: function(typeId, uid) |
| { |
| return this._profilesIdMap[this._makeKey(uid, typeId)]; |
| }, |
| |
| loadHeapSnapshot: function(uid, callback) |
| { |
| var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.HeapSnapshotProfileType.TypeId)]; |
| if (!profile) |
| return; |
| |
| if (!Preferences.detailedHeapProfiles) { |
| if (profile._loaded) |
| callback(profile); |
| else if (profile._is_loading) |
| profile._callbacks.push(callback); |
| else { |
| profile._is_loading = true; |
| profile._callbacks = [callback]; |
| profile._json = ""; |
| profile.sideBarElement.subtitle = WebInspector.UIString("Loading\u2026"); |
| ProfilerAgent.getProfile(profile.typeId, profile.uid); |
| } |
| } else { |
| if (!profile.proxy) |
| profile.proxy = new WebInspector.HeapSnapshotProxy(); |
| var proxy = profile.proxy; |
| if (proxy.startLoading(callback)) { |
| profile.sideBarElement.subtitle = WebInspector.UIString("Loading\u2026"); |
| ProfilerAgent.getProfile(profile.typeId, profile.uid); |
| } |
| } |
| }, |
| |
| _addHeapSnapshotChunk: function(uid, chunk) |
| { |
| var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.HeapSnapshotProfileType.TypeId)]; |
| if (!profile) |
| return; |
| if (!Preferences.detailedHeapProfiles) { |
| if (profile._loaded || !profile._is_loading) |
| return; |
| profile._json += chunk; |
| } else { |
| if (!profile.proxy) |
| return; |
| profile.proxy.pushJSONChunk(chunk); |
| } |
| }, |
| |
| _finishHeapSnapshot: function(uid) |
| { |
| var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.HeapSnapshotProfileType.TypeId)]; |
| if (!profile) |
| return; |
| if (!Preferences.detailedHeapProfiles) { |
| if (profile._loaded || !profile._is_loading) |
| return; |
| profile.sideBarElement.subtitle = WebInspector.UIString("Parsing\u2026"); |
| function doParse() |
| { |
| var loadedSnapshot = JSON.parse(profile._json); |
| var callbacks = profile._callbacks; |
| delete profile._callbacks; |
| delete profile._json; |
| delete profile._is_loading; |
| profile._loaded = true; |
| profile.sideBarElement.subtitle = ""; |
| |
| if (WebInspector.DetailedHeapshotView.prototype.isDetailedSnapshot(loadedSnapshot)) { |
| WebInspector.panels.profiles._enableDetailedHeapProfiles(false); |
| return; |
| } |
| |
| WebInspector.HeapSnapshotView.prototype.processLoadedSnapshot(profile, loadedSnapshot); |
| for (var i = 0; i < callbacks.length; ++i) |
| callbacks[i](profile); |
| } |
| setTimeout(doParse, 0); |
| } else { |
| if (!profile.proxy) |
| return; |
| var proxy = profile.proxy; |
| function parsed() |
| { |
| profile.sideBarElement.subtitle = Number.bytesToString(proxy.totalSize); |
| } |
| if (proxy.finishLoading(parsed)) |
| profile.sideBarElement.subtitle = WebInspector.UIString("Parsing\u2026"); |
| } |
| }, |
| |
| showView: function(view) |
| { |
| this.showProfile(view.profile); |
| }, |
| |
| getProfileType: function(typeId) |
| { |
| return this._profileTypesByIdMap[typeId]; |
| }, |
| |
| showProfileForURL: function(url) |
| { |
| var match = url.match(WebInspector.ProfileType.URLRegExp); |
| if (!match) |
| return; |
| this.showProfile(this._profilesIdMap[this._makeKey(match[3], match[1])]); |
| }, |
| |
| updateProfileTypeButtons: function() |
| { |
| for (var typeId in this._profileTypeButtonsByIdMap) { |
| var buttonElement = this._profileTypeButtonsByIdMap[typeId]; |
| var profileType = this.getProfileType(typeId); |
| buttonElement.className = profileType.buttonStyle; |
| buttonElement.title = profileType.buttonTooltip; |
| // FIXME: Apply profileType.buttonCaption once captions are added to button controls. |
| } |
| }, |
| |
| closeVisibleView: function() |
| { |
| if (this.visibleView) |
| this.visibleView.hide(); |
| delete this.visibleView; |
| }, |
| |
| displayTitleForProfileLink: function(title, typeId) |
| { |
| title = unescape(title); |
| if (title.indexOf(UserInitiatedProfileName) === 0) { |
| title = WebInspector.UIString("Profile %d", title.substring(UserInitiatedProfileName.length + 1)); |
| } else { |
| var titleKey = this._makeKey(title, typeId); |
| if (!(titleKey in this._profileGroupsForLinks)) |
| this._profileGroupsForLinks[titleKey] = 0; |
| |
| var groupNumber = ++this._profileGroupsForLinks[titleKey]; |
| |
| if (groupNumber > 2) |
| // The title is used in the console message announcing that a profile has started so it gets |
| // incremented twice as often as it's displayed |
| title += " " + WebInspector.UIString("Run %d", (groupNumber + 1) / 2); |
| } |
| |
| return title; |
| }, |
| |
| get searchableViews() |
| { |
| var views = []; |
| |
| const visibleView = this.visibleView; |
| if (visibleView && visibleView.performSearch) |
| views.push(visibleView); |
| |
| var profilesLength = this._profiles.length; |
| for (var i = 0; i < profilesLength; ++i) { |
| var profile = this._profiles[i]; |
| var view = profile.__profilesPanelProfileType.viewForProfile(profile); |
| if (!view.performSearch || view === visibleView) |
| continue; |
| views.push(view); |
| } |
| |
| return views; |
| }, |
| |
| searchMatchFound: function(view, matches) |
| { |
| view.profile._profilesTreeElement.searchMatches = matches; |
| }, |
| |
| searchCanceled: function(startingNewSearch) |
| { |
| WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch); |
| |
| if (!this._profiles) |
| return; |
| |
| for (var i = 0; i < this._profiles.length; ++i) { |
| var profile = this._profiles[i]; |
| profile._profilesTreeElement.searchMatches = 0; |
| } |
| }, |
| |
| _updateInterface: function() |
| { |
| // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change. |
| if (this._profilerEnabled) { |
| this.enableToggleButton.title = WebInspector.UIString("Profiling enabled. Click to disable."); |
| this.enableToggleButton.toggled = true; |
| for (var typeId in this._profileTypeButtonsByIdMap) |
| this._profileTypeButtonsByIdMap[typeId].removeStyleClass("hidden"); |
| this.profileViewStatusBarItemsContainer.removeStyleClass("hidden"); |
| this.clearResultsButton.element.removeStyleClass("hidden"); |
| this.panelEnablerView.visible = false; |
| } else { |
| this.enableToggleButton.title = WebInspector.UIString("Profiling disabled. Click to enable."); |
| this.enableToggleButton.toggled = false; |
| for (var typeId in this._profileTypeButtonsByIdMap) |
| this._profileTypeButtonsByIdMap[typeId].addStyleClass("hidden"); |
| this.profileViewStatusBarItemsContainer.addStyleClass("hidden"); |
| this.clearResultsButton.element.addStyleClass("hidden"); |
| this.panelEnablerView.visible = true; |
| } |
| }, |
| |
| _enableProfiling: function() |
| { |
| if (this._profilerEnabled) |
| return; |
| this._toggleProfiling(this.panelEnablerView.alwaysEnabled); |
| }, |
| |
| _toggleProfiling: function(optionalAlways) |
| { |
| if (this._profilerEnabled) { |
| WebInspector.settings.profilerEnabled = false; |
| ProfilerAgent.disable(); |
| } else { |
| WebInspector.settings.profilerEnabled = !!optionalAlways; |
| ProfilerAgent.enable(); |
| } |
| }, |
| |
| _populateProfiles: function() |
| { |
| if (!this._profilerEnabled || this._profilesWereRequested) |
| return; |
| |
| function populateCallback(error, profileHeaders) { |
| if (error) |
| return; |
| profileHeaders.sort(function(a, b) { return a.uid - b.uid; }); |
| var profileHeadersLength = profileHeaders.length; |
| for (var i = 0; i < profileHeadersLength; ++i) |
| if (!this.hasProfile(profileHeaders[i])) |
| this._addProfileHeader(profileHeaders[i]); |
| } |
| |
| ProfilerAgent.getProfileHeaders(populateCallback.bind(this)); |
| |
| this._profilesWereRequested = true; |
| }, |
| |
| updateMainViewWidth: function(width) |
| { |
| this.welcomeView.element.style.left = width + "px"; |
| this.profileViews.style.left = width + "px"; |
| this.profileViewStatusBarItemsContainer.style.left = Math.max(155, width) + "px"; |
| this.resize(); |
| }, |
| |
| _setRecordingProfile: function(isProfiling) |
| { |
| this.getProfileType(WebInspector.CPUProfileType.TypeId).setRecordingProfile(isProfiling); |
| if (this.hasTemporaryProfile(WebInspector.CPUProfileType.TypeId) !== isProfiling) { |
| if (!this._temporaryRecordingProfile) { |
| this._temporaryRecordingProfile = { |
| typeId: WebInspector.CPUProfileType.TypeId, |
| title: WebInspector.UIString("Recording…"), |
| uid: -1, |
| isTemporary: true |
| }; |
| } |
| if (isProfiling) |
| this._addProfileHeader(this._temporaryRecordingProfile); |
| else |
| this._removeProfileHeader(this._temporaryRecordingProfile); |
| } |
| this.updateProfileTypeButtons(); |
| }, |
| |
| takeHeapSnapshot: function(detailed) |
| { |
| if (!this.hasTemporaryProfile(WebInspector.HeapSnapshotProfileType.TypeId)) { |
| if (!this._temporaryTakingSnapshot) { |
| this._temporaryTakingSnapshot = { |
| typeId: WebInspector.HeapSnapshotProfileType.TypeId, |
| title: WebInspector.UIString("Snapshotting…"), |
| uid: -1, |
| isTemporary: true |
| }; |
| } |
| this._addProfileHeader(this._temporaryTakingSnapshot); |
| } |
| ProfilerAgent.takeHeapSnapshot(detailed); |
| }, |
| |
| _reportHeapSnapshotProgress: function(done, total) |
| { |
| if (this.hasTemporaryProfile(WebInspector.HeapSnapshotProfileType.TypeId)) { |
| this._temporaryTakingSnapshot.sideBarElement.subtitle = WebInspector.UIString("%.2f%%", (done / total) * 100); |
| if (done >= total) |
| this._removeProfileHeader(this._temporaryTakingSnapshot); |
| } |
| }, |
| |
| handleShortcut: function(event) |
| { |
| if (!Preferences.heapProfilerPresent || Preferences.detailedHeapProfiles) |
| return; |
| var combo = ["U+004C", "U+0045", "U+0041", "U+004B", "U+005A"]; // "LEAKZ" |
| if (this._recognizeKeyboardCombo(combo, event)) { |
| this._displayDetailedHeapProfilesEnabledHint(); |
| this._enableDetailedHeapProfiles(true); |
| } |
| }, |
| |
| _recognizeKeyboardCombo: function(combo, event) |
| { |
| var isRecognized = false; |
| if (!this._comboPosition) { |
| if (event.keyIdentifier === combo[0]) |
| this._comboPosition = 1; |
| } else if (event.keyIdentifier === combo[this._comboPosition]) { |
| if (++this._comboPosition === combo.length) |
| isRecognized = true; |
| } else |
| delete this._comboPosition; |
| if (this._comboPosition) |
| event.handled = true; |
| return isRecognized; |
| }, |
| |
| _displayDetailedHeapProfilesEnabledHint: function() |
| { |
| var message = new WebInspector.HelpScreen("Congratulations!"); |
| message.contentElement.addStyleClass("help-table"); |
| message.contentElement.textContent = "Detailed Heap snapshots are now enabled."; |
| message.show(); |
| |
| function hideHint() |
| { |
| message._hide(); |
| } |
| |
| setTimeout(hideHint, 2000); |
| }, |
| |
| _enableDetailedHeapProfiles: function(resetAgent) |
| { |
| if (resetAgent) |
| this._clearProfiles(); |
| else |
| this._reset(); |
| var oldProfileType = this._profileTypesByIdMap[WebInspector.HeapSnapshotProfileType.TypeId]; |
| var profileType = new WebInspector.DetailedHeapshotProfileType(); |
| profileType.treeElement = oldProfileType.treeElement; |
| this._profileTypesByIdMap[profileType.id] = profileType; |
| Preferences.detailedHeapProfiles = true; |
| this.hide(); |
| this.show(); |
| } |
| } |
| |
| WebInspector.ProfilesPanel.prototype.__proto__ = WebInspector.Panel.prototype; |
| |
| |
| WebInspector.ProfilerDispatcher = function(profiler) |
| { |
| this._profiler = profiler; |
| } |
| |
| WebInspector.ProfilerDispatcher.prototype = { |
| profilerWasEnabled: function() |
| { |
| this._profiler._profilerWasEnabled(); |
| }, |
| |
| profilerWasDisabled: function() |
| { |
| this._profiler._profilerWasDisabled(); |
| }, |
| |
| resetProfiles: function() |
| { |
| this._profiler._reset(); |
| }, |
| |
| addProfileHeader: function(profile) |
| { |
| this._profiler._addProfileHeader(profile); |
| }, |
| |
| addHeapSnapshotChunk: function(uid, chunk) |
| { |
| this._profiler._addHeapSnapshotChunk(uid, chunk); |
| }, |
| |
| finishHeapSnapshot: function(uid) |
| { |
| this._profiler._finishHeapSnapshot(uid); |
| }, |
| |
| setRecordingProfile: function(isProfiling) |
| { |
| this._profiler._setRecordingProfile(isProfiling); |
| }, |
| |
| reportHeapSnapshotProgress: function(done, total) |
| { |
| this._profiler._reportHeapSnapshotProgress(done, total); |
| } |
| } |
| |
| WebInspector.ProfileSidebarTreeElement = function(profile, titleFormat, className) |
| { |
| this.profile = profile; |
| this._titleFormat = titleFormat; |
| |
| if (this.profile.title.indexOf(UserInitiatedProfileName) === 0) |
| this._profileNumber = this.profile.title.substring(UserInitiatedProfileName.length + 1); |
| |
| WebInspector.SidebarTreeElement.call(this, className, "", "", profile, false); |
| |
| this.refreshTitles(); |
| } |
| |
| WebInspector.ProfileSidebarTreeElement.prototype = { |
| onselect: function() |
| { |
| this.treeOutline.panel.showProfile(this.profile); |
| }, |
| |
| ondelete: function() |
| { |
| this.treeOutline.panel._removeProfileHeader(this.profile); |
| return true; |
| }, |
| |
| get mainTitle() |
| { |
| if (this._mainTitle) |
| return this._mainTitle; |
| if (this.profile.title.indexOf(UserInitiatedProfileName) === 0) |
| return WebInspector.UIString(this._titleFormat, this._profileNumber); |
| return this.profile.title; |
| }, |
| |
| set mainTitle(x) |
| { |
| this._mainTitle = x; |
| this.refreshTitles(); |
| }, |
| |
| set searchMatches(matches) |
| { |
| if (!matches) { |
| if (!this.bubbleElement) |
| return; |
| this.bubbleElement.removeStyleClass("search-matches"); |
| this.bubbleText = ""; |
| return; |
| } |
| |
| this.bubbleText = matches; |
| this.bubbleElement.addStyleClass("search-matches"); |
| } |
| } |
| |
| WebInspector.ProfileSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; |
| |
| WebInspector.ProfileGroupSidebarTreeElement = function(title, subtitle) |
| { |
| WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true); |
| } |
| |
| WebInspector.ProfileGroupSidebarTreeElement.prototype = { |
| onselect: function() |
| { |
| if (this.children.length > 0) |
| WebInspector.panels.profiles.showProfile(this.children[this.children.length - 1].profile); |
| } |
| } |
| |
| WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; |