2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
28 WebInspector.ProfileType = function(id, name)
34 WebInspector.ProfileType.URLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/;
36 WebInspector.ProfileType.prototype = {
62 buttonClicked: function()
66 viewForProfile: function(profile)
68 if (!profile._profileView)
69 profile._profileView = this.createView(profile);
70 return profile._profileView;
78 // Must be implemented by subclasses.
79 createView: function(profile)
81 throw new Error("Needs implemented.");
84 // Must be implemented by subclasses.
85 createSidebarTreeElementForProfile: function(profile)
87 throw new Error("Needs implemented.");
91 WebInspector.registerLinkifierPlugin(function(title)
93 var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title);
94 if (profileStringMatches)
95 title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
99 WebInspector.ProfilesPanel = function()
101 WebInspector.Panel.call(this, "profiles");
102 this.registerRequiredCSS("heapProfiler.css");
103 this.registerRequiredCSS("profilesPanel.css");
105 this.createSplitViewWithSidebarTree();
107 this._profileTypesByIdMap = {};
108 this._profileTypeButtonsByIdMap = {};
110 var panelEnablerHeading = WebInspector.UIString("You need to enable profiling before you can use the Profiles panel.");
111 var panelEnablerDisclaimer = WebInspector.UIString("Enabling profiling will make scripts run slower.");
112 var panelEnablerButton = WebInspector.UIString("Enable Profiling");
113 this.panelEnablerView = new WebInspector.PanelEnablerView("profiles", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
114 this.panelEnablerView.addEventListener("enable clicked", this.enableProfiler, this);
116 this.profileViews = document.createElement("div");
117 this.profileViews.id = "profile-views";
118 this.splitView.mainElement.appendChild(this.profileViews);
120 this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
121 this.enableToggleButton.addEventListener("click", this._toggleProfiling.bind(this), false);
122 if (Capabilities.profilerAlwaysEnabled)
123 this.enableToggleButton.element.addStyleClass("hidden");
125 this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");
126 this.clearResultsButton.addEventListener("click", this._clearProfiles.bind(this), false);
128 this.profileViewStatusBarItemsContainer = document.createElement("div");
129 this.profileViewStatusBarItemsContainer.className = "status-bar-items";
131 this.welcomeView = new WebInspector.WelcomeView("profiles", WebInspector.UIString("Welcome to the Profiles panel"));
134 this._profilerEnabled = Capabilities.profilerAlwaysEnabled;
137 this._registerProfileType(new WebInspector.CPUProfileType());
138 if (Capabilities.heapProfilerPresent)
139 this._registerProfileType(new WebInspector.DetailedHeapshotProfileType());
141 InspectorBackend.registerProfilerDispatcher(new WebInspector.ProfilerDispatcher(this));
143 if (Capabilities.profilerAlwaysEnabled || WebInspector.settings.profilerEnabled.get())
144 ProfilerAgent.enable();
146 function onProfilerEnebled(error, value) {
148 this._profilerWasEnabled();
150 ProfilerAgent.isEnabled(onProfilerEnebled.bind(this));
154 WebInspector.ProfilesPanel.prototype = {
155 get toolbarItemLabel()
157 return WebInspector.UIString("Profiles");
162 function clickHandler(profileType, buttonElement)
164 profileType.buttonClicked.call(profileType);
165 this.updateProfileTypeButtons();
168 var items = [this.enableToggleButton.element];
169 // FIXME: Generate a single "combo-button".
170 for (var typeId in this._profileTypesByIdMap) {
171 var profileType = this.getProfileType(typeId);
172 if (profileType.buttonStyle) {
173 var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption);
174 this._profileTypeButtonsByIdMap[typeId] = button.element;
175 button.element.addEventListener("click", clickHandler.bind(this, profileType, button.element), false);
176 items.push(button.element);
179 items.push(this.clearResultsButton.element, this.profileViewStatusBarItemsContainer);
185 WebInspector.Panel.prototype.wasShown.call(this);
186 this._populateProfiles();
189 _profilerWasEnabled: function()
191 if (this._profilerEnabled)
194 this._profilerEnabled = true;
197 if (this.isShowing())
198 this._populateProfiles();
201 _profilerWasDisabled: function()
203 if (!this._profilerEnabled)
206 this._profilerEnabled = false;
212 WebInspector.Panel.prototype.reset.call(this);
214 for (var i = 0; i < this._profiles.length; ++i) {
215 var view = this._profiles[i]._profileView;
218 if ("dispose" in view)
222 delete this._profiles[i]._profileView;
224 delete this.visibleView;
226 delete this.currentQuery;
227 this.searchCanceled();
230 this._profilesIdMap = {};
231 this._profileGroups = {};
232 this._profileGroupsForLinks = {};
233 this._profilesWereRequested = false;
235 this.sidebarTreeElement.removeStyleClass("some-expandable");
237 for (var typeId in this._profileTypesByIdMap)
238 this.getProfileType(typeId).treeElement.removeChildren();
240 this.profileViews.removeChildren();
242 this.profileViewStatusBarItemsContainer.removeChildren();
244 this.removeAllListeners();
246 this._updateInterface();
247 this.welcomeView.show(this.splitView.mainElement);
250 _clearProfiles: function()
252 ProfilerAgent.clearProfiles();
256 _registerProfileType: function(profileType)
258 this._profileTypesByIdMap[profileType.id] = profileType;
259 profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.name, null, true);
260 this.sidebarTree.appendChild(profileType.treeElement);
261 profileType.treeElement.expand();
262 this._addWelcomeMessage(profileType);
265 _addWelcomeMessage: function(profileType)
267 var message = profileType.welcomeMessage;
268 // Message text is supposed to have a '%s' substring as a placeholder
269 // for a status bar button. If it is there, we split the message in two
270 // parts, and insert the button between them.
271 var buttonPos = message.indexOf("%s");
272 if (buttonPos > -1) {
273 var container = document.createDocumentFragment();
274 var part1 = document.createElement("span");
275 part1.textContent = message.substr(0, buttonPos);
276 container.appendChild(part1);
278 var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption);
279 container.appendChild(button.element);
281 var part2 = document.createElement("span");
282 part2.textContent = message.substr(buttonPos + 2);
283 container.appendChild(part2);
284 this.welcomeView.addMessage(container);
286 this.welcomeView.addMessage(message);
289 _makeKey: function(text, profileTypeId)
291 return escape(text) + '/' + escape(profileTypeId);
294 _addProfileHeader: function(profile)
296 if (this.hasTemporaryProfile(profile.typeId)) {
297 if (profile.typeId === WebInspector.CPUProfileType.TypeId)
298 this._removeProfileHeader(this._temporaryRecordingProfile);
300 this._removeProfileHeader(this._temporaryTakingSnapshot);
303 var typeId = profile.typeId;
304 var profileType = this.getProfileType(typeId);
305 var sidebarParent = profileType.treeElement;
309 profile.__profilesPanelProfileType = profileType;
310 this._profiles.push(profile);
311 this._profilesIdMap[this._makeKey(profile.uid, typeId)] = profile;
313 if (profile.title.indexOf(UserInitiatedProfileName) !== 0) {
314 var profileTitleKey = this._makeKey(profile.title, typeId);
315 if (!(profileTitleKey in this._profileGroups))
316 this._profileGroups[profileTitleKey] = [];
318 var group = this._profileGroups[profileTitleKey];
321 if (group.length === 2) {
322 // Make a group TreeElement now that there are 2 profiles.
323 group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title);
325 // Insert at the same index for the first profile of the group.
326 var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement);
327 sidebarParent.insertChild(group._profilesTreeElement, index);
329 // Move the first profile to the group.
330 var selected = group[0]._profilesTreeElement.selected;
331 sidebarParent.removeChild(group[0]._profilesTreeElement);
332 group._profilesTreeElement.appendChild(group[0]._profilesTreeElement);
334 group[0]._profilesTreeElement.revealAndSelect();
336 group[0]._profilesTreeElement.small = true;
337 group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
339 this.sidebarTreeElement.addStyleClass("some-expandable");
342 if (group.length >= 2) {
343 sidebarParent = group._profilesTreeElement;
344 alternateTitle = WebInspector.UIString("Run %d", group.length);
349 var profileTreeElement = profileType.createSidebarTreeElementForProfile(profile);
350 profile.sidebarElement = profileTreeElement;
351 profileTreeElement.small = small;
353 profileTreeElement.mainTitle = alternateTitle;
354 profile._profilesTreeElement = profileTreeElement;
356 sidebarParent.appendChild(profileTreeElement);
357 if (!profile.isTemporary) {
358 this.welcomeView.detach();
359 if (!this.visibleView)
360 this.showProfile(profile);
361 this.dispatchEventToListeners("profile added");
365 _removeProfileHeader: function(profile)
367 var typeId = profile.typeId;
368 var profileType = this.getProfileType(typeId);
369 var sidebarParent = profileType.treeElement;
371 for (var i = 0; i < this._profiles.length; ++i) {
372 if (this._profiles[i].uid === profile.uid) {
373 profile = this._profiles[i];
374 this._profiles.splice(i, 1);
378 delete this._profilesIdMap[this._makeKey(profile.uid, typeId)];
380 var profileTitleKey = this._makeKey(profile.title, typeId);
381 delete this._profileGroups[profileTitleKey];
383 sidebarParent.removeChild(profile._profilesTreeElement);
385 if (!profile.isTemporary)
386 ProfilerAgent.removeProfile(profile.typeId, profile.uid);
388 // No other item will be selected if there aren't any other profiles, so
389 // make sure that view gets cleared when the last profile is removed.
390 if (!this._profiles.length)
391 this.closeVisibleView();
394 showProfile: function(profile)
396 if (!profile || profile.isTemporary)
399 this.closeVisibleView();
401 var view = profile.__profilesPanelProfileType.viewForProfile(profile);
403 view.show(this.profileViews);
405 profile._profilesTreeElement._suppressOnSelect = true;
406 profile._profilesTreeElement.revealAndSelect();
407 delete profile._profilesTreeElement._suppressOnSelect;
409 this.visibleView = view;
411 this.profileViewStatusBarItemsContainer.removeChildren();
413 var statusBarItems = view.statusBarItems;
415 for (var i = 0; i < statusBarItems.length; ++i)
416 this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
419 getProfiles: function(typeId)
422 var profilesCount = this._profiles.length;
423 for (var i = 0; i < profilesCount; ++i) {
424 var profile = this._profiles[i];
425 if (!profile.isTemporary && profile.typeId === typeId)
426 result.push(profile);
431 hasTemporaryProfile: function(typeId)
433 var profilesCount = this._profiles.length;
434 for (var i = 0; i < profilesCount; ++i)
435 if (this._profiles[i].typeId === typeId && this._profiles[i].isTemporary)
440 hasProfile: function(profile)
442 return !!this._profilesIdMap[this._makeKey(profile.uid, profile.typeId)];
445 getProfile: function(typeId, uid)
447 return this._profilesIdMap[this._makeKey(uid, typeId)];
450 loadHeapSnapshot: function(uid, callback)
452 var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
456 if (!profile.proxy) {
457 function setProfileWait(event) {
458 profile.sidebarElement.wait = event.data;
460 var worker = new WebInspector.HeapSnapshotWorker();
461 worker.addEventListener("wait", setProfileWait, this);
462 profile.proxy = worker.createObject("WebInspector.HeapSnapshotLoader");
464 var proxy = profile.proxy;
465 if (proxy.startLoading(callback)) {
466 profile.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026");
467 profile.sidebarElement.wait = true;
468 ProfilerAgent.getProfile(profile.typeId, profile.uid);
472 _addHeapSnapshotChunk: function(uid, chunk)
474 var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
475 if (!profile || !profile.proxy)
477 profile.proxy.pushJSONChunk(chunk);
480 _finishHeapSnapshot: function(uid)
482 var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
483 if (!profile || !profile.proxy)
485 var proxy = profile.proxy;
486 function parsed(snapshotProxy)
488 profile.proxy = snapshotProxy;
489 profile.sidebarElement.subtitle = Number.bytesToString(snapshotProxy.totalSize);
490 profile.sidebarElement.wait = false;
491 snapshotProxy.worker.startCheckingForLongRunningCalls();
493 if (proxy.finishLoading(parsed))
494 profile.sidebarElement.subtitle = WebInspector.UIString("Parsing\u2026");
497 showView: function(view)
499 this.showProfile(view.profile);
502 getProfileType: function(typeId)
504 return this._profileTypesByIdMap[typeId];
507 showProfileForURL: function(url)
509 var match = url.match(WebInspector.ProfileType.URLRegExp);
512 this.showProfile(this._profilesIdMap[this._makeKey(match[3], match[1])]);
515 updateProfileTypeButtons: function()
517 for (var typeId in this._profileTypeButtonsByIdMap) {
518 var buttonElement = this._profileTypeButtonsByIdMap[typeId];
519 var profileType = this.getProfileType(typeId);
520 buttonElement.className = profileType.buttonStyle;
521 buttonElement.title = profileType.buttonTooltip;
522 // FIXME: Apply profileType.buttonCaption once captions are added to button controls.
526 closeVisibleView: function()
528 if (this.visibleView)
529 this.visibleView.detach();
530 delete this.visibleView;
533 displayTitleForProfileLink: function(title, typeId)
535 title = unescape(title);
536 if (title.indexOf(UserInitiatedProfileName) === 0) {
537 title = WebInspector.UIString("Profile %d", title.substring(UserInitiatedProfileName.length + 1));
539 var titleKey = this._makeKey(title, typeId);
540 if (!(titleKey in this._profileGroupsForLinks))
541 this._profileGroupsForLinks[titleKey] = 0;
543 var groupNumber = ++this._profileGroupsForLinks[titleKey];
546 // The title is used in the console message announcing that a profile has started so it gets
547 // incremented twice as often as it's displayed
548 title += " " + WebInspector.UIString("Run %d", (groupNumber + 1) / 2);
554 performSearch: function(query)
556 this.searchCanceled();
558 var searchableViews = this._searchableViews();
559 if (!searchableViews || !searchableViews.length)
562 var parentElement = this.viewsContainerElement;
563 var visibleView = this.visibleView;
564 var sortFuction = this.searchResultsSortFunction;
566 var matchesCountUpdateTimeout = null;
568 function updateMatchesCount()
570 WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this);
571 matchesCountUpdateTimeout = null;
574 function updateMatchesCountSoon()
576 if (matchesCountUpdateTimeout)
578 // Update the matches count every half-second so it doesn't feel twitchy.
579 matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
582 function finishedCallback(view, searchMatches)
587 this._totalSearchMatches += searchMatches;
588 this._searchResults.push(view);
591 this._searchResults.sort(sortFuction);
593 if (this.searchMatchFound)
594 this.searchMatchFound(view, searchMatches);
596 updateMatchesCountSoon.call(this);
598 if (view === visibleView)
599 view.jumpToFirstSearchResult();
604 var boundFinishedCallback = finishedCallback.bind(this);
605 var chunkIntervalIdentifier = null;
607 // Split up the work into chunks so we don't block the
608 // UI thread while processing.
610 function processChunk()
612 var view = searchableViews[i];
614 if (++i >= searchableViews.length) {
615 if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
616 delete panel._currentSearchChunkIntervalIdentifier;
617 clearInterval(chunkIntervalIdentifier);
623 view.currentQuery = query;
624 view.performSearch(query, boundFinishedCallback);
629 chunkIntervalIdentifier = setInterval(processChunk, 25);
630 this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
633 jumpToNextSearchResult: function()
635 if (!this.showView || !this._searchResults || !this._searchResults.length)
638 var showFirstResult = false;
640 this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
641 if (this._currentSearchResultIndex === -1) {
642 this._currentSearchResultIndex = 0;
643 showFirstResult = true;
646 var currentView = this._searchResults[this._currentSearchResultIndex];
648 if (currentView.showingLastSearchResult()) {
649 if (++this._currentSearchResultIndex >= this._searchResults.length)
650 this._currentSearchResultIndex = 0;
651 currentView = this._searchResults[this._currentSearchResultIndex];
652 showFirstResult = true;
655 if (currentView !== this.visibleView) {
656 this.showView(currentView);
657 WebInspector.searchController.focusSearchField();
661 currentView.jumpToFirstSearchResult();
663 currentView.jumpToNextSearchResult();
666 jumpToPreviousSearchResult: function()
668 if (!this.showView || !this._searchResults || !this._searchResults.length)
671 var showLastResult = false;
673 this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
674 if (this._currentSearchResultIndex === -1) {
675 this._currentSearchResultIndex = 0;
676 showLastResult = true;
679 var currentView = this._searchResults[this._currentSearchResultIndex];
681 if (currentView.showingFirstSearchResult()) {
682 if (--this._currentSearchResultIndex < 0)
683 this._currentSearchResultIndex = (this._searchResults.length - 1);
684 currentView = this._searchResults[this._currentSearchResultIndex];
685 showLastResult = true;
688 if (currentView !== this.visibleView) {
689 this.showView(currentView);
690 WebInspector.searchController.focusSearchField();
694 currentView.jumpToLastSearchResult();
696 currentView.jumpToPreviousSearchResult();
699 _searchableViews: function()
703 const visibleView = this.visibleView;
704 if (visibleView && visibleView.performSearch)
705 views.push(visibleView);
707 var profilesLength = this._profiles.length;
708 for (var i = 0; i < profilesLength; ++i) {
709 var profile = this._profiles[i];
710 var view = profile.__profilesPanelProfileType.viewForProfile(profile);
711 if (!view.performSearch || view === visibleView)
719 searchMatchFound: function(view, matches)
721 view.profile._profilesTreeElement.searchMatches = matches;
724 searchCanceled: function()
726 if (this._searchResults) {
727 for (var i = 0; i < this._searchResults.length; ++i) {
728 var view = this._searchResults[i];
729 if (view.searchCanceled)
730 view.searchCanceled();
731 delete view.currentQuery;
735 WebInspector.Panel.prototype.searchCanceled.call(this);
737 if (this._currentSearchChunkIntervalIdentifier) {
738 clearInterval(this._currentSearchChunkIntervalIdentifier);
739 delete this._currentSearchChunkIntervalIdentifier;
742 this._totalSearchMatches = 0;
743 this._currentSearchResultIndex = 0;
744 this._searchResults = [];
749 for (var i = 0; i < this._profiles.length; ++i) {
750 var profile = this._profiles[i];
751 profile._profilesTreeElement.searchMatches = 0;
755 _updateInterface: function()
757 // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change.
758 if (this._profilerEnabled) {
759 this.enableToggleButton.title = WebInspector.UIString("Profiling enabled. Click to disable.");
760 this.enableToggleButton.toggled = true;
761 for (var typeId in this._profileTypeButtonsByIdMap)
762 this._profileTypeButtonsByIdMap[typeId].removeStyleClass("hidden");
763 this.profileViewStatusBarItemsContainer.removeStyleClass("hidden");
764 this.clearResultsButton.element.removeStyleClass("hidden");
765 this.panelEnablerView.detach();
767 this.enableToggleButton.title = WebInspector.UIString("Profiling disabled. Click to enable.");
768 this.enableToggleButton.toggled = false;
769 for (var typeId in this._profileTypeButtonsByIdMap)
770 this._profileTypeButtonsByIdMap[typeId].addStyleClass("hidden");
771 this.profileViewStatusBarItemsContainer.addStyleClass("hidden");
772 this.clearResultsButton.element.addStyleClass("hidden");
773 this.panelEnablerView.show(this.element);
777 get profilerEnabled()
779 return this._profilerEnabled;
782 enableProfiler: function()
784 if (this._profilerEnabled)
786 this._toggleProfiling(this.panelEnablerView.alwaysEnabled);
789 disableProfiler: function()
791 if (!this._profilerEnabled)
793 this._toggleProfiling(this.panelEnablerView.alwaysEnabled);
796 _toggleProfiling: function(optionalAlways)
798 if (this._profilerEnabled) {
799 WebInspector.settings.profilerEnabled.set(false);
800 ProfilerAgent.disable();
802 WebInspector.settings.profilerEnabled.set(!!optionalAlways);
803 ProfilerAgent.enable();
807 _populateProfiles: function()
809 if (!this._profilerEnabled || this._profilesWereRequested)
812 function populateCallback(error, profileHeaders) {
815 profileHeaders.sort(function(a, b) { return a.uid - b.uid; });
816 var profileHeadersLength = profileHeaders.length;
817 for (var i = 0; i < profileHeadersLength; ++i)
818 if (!this.hasProfile(profileHeaders[i]))
819 this._addProfileHeader(profileHeaders[i]);
822 ProfilerAgent.getProfileHeaders(populateCallback.bind(this));
824 this._profilesWereRequested = true;
827 sidebarResized: function(event)
829 var width = event.data;
830 // Min width = <number of buttons on the left> * 31
831 this.profileViewStatusBarItemsContainer.style.left = Math.max(6 * 31, width) + "px";
834 _setRecordingProfile: function(isProfiling)
836 this.getProfileType(WebInspector.CPUProfileType.TypeId).setRecordingProfile(isProfiling);
837 if (this.hasTemporaryProfile(WebInspector.CPUProfileType.TypeId) !== isProfiling) {
838 if (!this._temporaryRecordingProfile) {
839 this._temporaryRecordingProfile = {
840 typeId: WebInspector.CPUProfileType.TypeId,
841 title: WebInspector.UIString("Recording…"),
847 this._addProfileHeader(this._temporaryRecordingProfile);
848 WebInspector.userMetrics.ProfilesCPUProfileTaken.record();
850 this._removeProfileHeader(this._temporaryRecordingProfile);
852 this.updateProfileTypeButtons();
855 takeHeapSnapshot: function()
857 if (!this.hasTemporaryProfile(WebInspector.DetailedHeapshotProfileType.TypeId)) {
858 if (!this._temporaryTakingSnapshot) {
859 this._temporaryTakingSnapshot = {
860 typeId: WebInspector.DetailedHeapshotProfileType.TypeId,
861 title: WebInspector.UIString("Snapshotting…"),
866 this._addProfileHeader(this._temporaryTakingSnapshot);
868 ProfilerAgent.takeHeapSnapshot();
869 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
872 _reportHeapSnapshotProgress: function(done, total)
874 if (this.hasTemporaryProfile(WebInspector.DetailedHeapshotProfileType.TypeId)) {
875 this._temporaryTakingSnapshot.sidebarElement.subtitle = WebInspector.UIString("%.2f%%", (done / total) * 100);
876 this._temporaryTakingSnapshot.sidebarElement.wait = true;
878 this._removeProfileHeader(this._temporaryTakingSnapshot);
882 _enableDetailedHeapProfiles: function(resetAgent)
885 this._clearProfiles();
888 var oldProfileType = this._profileTypesByIdMap[WebInspector.DetailedHeapshotProfileType.TypeId];
889 var profileType = new WebInspector.DetailedHeapshotProfileType();
890 profileType.treeElement = oldProfileType.treeElement;
891 this._profileTypesByIdMap[profileType.id] = profileType;
892 Capabilities.detailedHeapProfiles = true;
898 WebInspector.ProfilesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
901 WebInspector.ProfilerDispatcher = function(profiler)
903 this._profiler = profiler;
906 WebInspector.ProfilerDispatcher.prototype = {
907 profilerWasEnabled: function()
909 this._profiler._profilerWasEnabled();
912 profilerWasDisabled: function()
914 this._profiler._profilerWasDisabled();
917 resetProfiles: function()
919 this._profiler._reset();
922 addProfileHeader: function(profile)
924 this._profiler._addProfileHeader(profile);
927 addHeapSnapshotChunk: function(uid, chunk)
929 this._profiler._addHeapSnapshotChunk(uid, chunk);
932 finishHeapSnapshot: function(uid)
934 this._profiler._finishHeapSnapshot(uid);
937 setRecordingProfile: function(isProfiling)
939 this._profiler._setRecordingProfile(isProfiling);
942 reportHeapSnapshotProgress: function(done, total)
944 this._profiler._reportHeapSnapshotProgress(done, total);
948 WebInspector.ProfileSidebarTreeElement = function(profile, titleFormat, className)
950 this.profile = profile;
951 this._titleFormat = titleFormat;
953 if (this.profile.title.indexOf(UserInitiatedProfileName) === 0)
954 this._profileNumber = this.profile.title.substring(UserInitiatedProfileName.length + 1);
956 WebInspector.SidebarTreeElement.call(this, className, "", "", profile, false);
958 this.refreshTitles();
961 WebInspector.ProfileSidebarTreeElement.prototype = {
964 if (!this._suppressOnSelect)
965 this.treeOutline.panel.showProfile(this.profile);
970 this.treeOutline.panel._removeProfileHeader(this.profile);
977 return this._mainTitle;
978 if (this.profile.title.indexOf(UserInitiatedProfileName) === 0)
979 return WebInspector.UIString(this._titleFormat, this._profileNumber);
980 return this.profile.title;
986 this.refreshTitles();
989 set searchMatches(matches)
992 if (!this.bubbleElement)
994 this.bubbleElement.removeStyleClass("search-matches");
995 this.bubbleText = "";
999 this.bubbleText = matches;
1000 this.bubbleElement.addStyleClass("search-matches");
1004 WebInspector.ProfileSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
1006 WebInspector.ProfileGroupSidebarTreeElement = function(title, subtitle)
1008 WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
1011 WebInspector.ProfileGroupSidebarTreeElement.prototype = {
1012 onselect: function()
1014 if (this.children.length > 0)
1015 WebInspector.panels.profiles.showProfile(this.children[this.children.length - 1].profile);
1019 WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;