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.
28 * @extends {WebInspector.Object}
30 * @param {string} name
32 WebInspector.ProfileType = function(id, name)
34 WebInspector.Object.call(this);
37 /** @type {!Array.<!WebInspector.ProfileHeader>} */
39 /** @type {?WebInspector.ProfileHeader} */
40 this._profileBeingRecorded = null;
41 this._nextProfileUid = 1;
43 window.addEventListener("unload", this._clearTempStorage.bind(this), false);
49 WebInspector.ProfileType.Events = {
50 AddProfileHeader: "add-profile-header",
51 ProfileComplete: "profile-complete",
52 RemoveProfileHeader: "remove-profile-header",
53 ViewUpdated: "view-updated"
56 WebInspector.ProfileType.prototype = {
60 hasTemporaryView: function()
68 fileExtension: function()
101 buttonClicked: function()
114 isInstantProfile: function()
122 isEnabled: function()
128 * @return {!Array.<!WebInspector.ProfileHeader>}
130 getProfiles: function()
133 * @param {!WebInspector.ProfileHeader} profile
135 * @this {WebInspector.ProfileType}
137 function isFinished(profile)
139 return this._profileBeingRecorded !== profile;
141 return this._profiles.filter(isFinished.bind(this));
147 decorationElement: function()
154 * @param {number} uid
155 * @return {?WebInspector.ProfileHeader}
157 getProfile: function(uid)
160 for (var i = 0; i < this._profiles.length; ++i) {
161 if (this._profiles[i].uid === uid)
162 return this._profiles[i];
168 * @param {!File} file
170 loadFromFile: function(file)
172 var name = file.name;
173 if (name.endsWith(this.fileExtension()))
174 name = name.substr(0, name.length - this.fileExtension().length);
175 var profile = this.createProfileLoadedFromFile(name);
176 profile.setFromFile();
177 this.setProfileBeingRecorded(profile);
178 this.addProfile(profile);
179 profile.loadFromFile(file);
183 * @param {!string} title
184 * @return {!WebInspector.ProfileHeader}
186 createProfileLoadedFromFile: function(title)
188 throw new Error("Needs implemented.");
192 * @param {!WebInspector.ProfileHeader} profile
194 addProfile: function(profile)
196 this._profiles.push(profile);
197 this.dispatchEventToListeners(WebInspector.ProfileType.Events.AddProfileHeader, profile);
201 * @param {!WebInspector.ProfileHeader} profile
203 removeProfile: function(profile)
205 var index = this._profiles.indexOf(profile);
208 this._profiles.splice(index, 1);
209 this._disposeProfile(profile);
212 _clearTempStorage: function()
214 for (var i = 0; i < this._profiles.length; ++i)
215 this._profiles[i].removeTempFile();
220 * @return {?WebInspector.ProfileHeader}
222 profileBeingRecorded: function()
224 return this._profileBeingRecorded;
228 * @param {?WebInspector.ProfileHeader} profile
230 setProfileBeingRecorded: function(profile)
232 if (this._profileBeingRecorded)
233 this._profileBeingRecorded.target().profilingLock.release();
235 profile.target().profilingLock.acquire();
236 this._profileBeingRecorded = profile;
239 profileBeingRecordedRemoved: function()
245 var profiles = this._profiles.slice(0);
246 for (var i = 0; i < profiles.length; ++i)
247 this._disposeProfile(profiles[i]);
250 this._nextProfileUid = 1;
254 * @param {!WebInspector.ProfileHeader} profile
256 _disposeProfile: function(profile)
258 this.dispatchEventToListeners(WebInspector.ProfileType.Events.RemoveProfileHeader, profile);
260 if (this._profileBeingRecorded === profile) {
261 this.profileBeingRecordedRemoved();
262 this.setProfileBeingRecorded(null);
266 __proto__: WebInspector.Object.prototype
272 WebInspector.ProfileType.DataDisplayDelegate = function()
276 WebInspector.ProfileType.DataDisplayDelegate.prototype = {
278 * @param {?WebInspector.ProfileHeader} profile
279 * @return {?WebInspector.View}
281 showProfile: function(profile) { },
284 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
285 * @param {string} perspectiveName
287 showObject: function(snapshotObjectId, perspectiveName) { }
292 * @extends {WebInspector.TargetAwareObject}
293 * @param {!WebInspector.ProfileType} profileType
294 * @param {string} title
296 WebInspector.ProfileHeader = function(target, profileType, title)
298 WebInspector.TargetAwareObject.call(this, target);
299 this._profileType = profileType;
301 this.uid = profileType._nextProfileUid++;
302 this._fromFile = false;
307 * @param {?string} subtitle
308 * @param {boolean|undefined} wait
310 WebInspector.ProfileHeader.StatusUpdate = function(subtitle, wait)
312 /** @type {?string} */
313 this.subtitle = subtitle;
314 /** @type {boolean|undefined} */
318 WebInspector.ProfileHeader.Events = {
319 UpdateStatus: "UpdateStatus",
320 ProfileReceived: "ProfileReceived"
323 WebInspector.ProfileHeader.prototype = {
325 * @return {!WebInspector.ProfileType}
327 profileType: function()
329 return this._profileType;
333 * @param {?string} subtitle
334 * @param {boolean=} wait
336 updateStatus: function(subtitle, wait)
338 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.UpdateStatus, new WebInspector.ProfileHeader.StatusUpdate(subtitle, wait));
342 * Must be implemented by subclasses.
343 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
344 * @return {!WebInspector.ProfileSidebarTreeElement}
346 createSidebarTreeElement: function(dataDisplayDelegate)
348 throw new Error("Needs implemented.");
352 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
353 * @return {!WebInspector.View}
355 createView: function(dataDisplayDelegate)
357 throw new Error("Not implemented.");
360 removeTempFile: function()
363 this._tempFile.remove();
371 * @param {!Function} callback
373 load: function(callback)
380 canSaveToFile: function()
385 saveToFile: function()
387 throw new Error("Needs implemented");
391 * @param {!File} file
393 loadFromFile: function(file)
395 throw new Error("Needs implemented");
403 return this._fromFile;
406 setFromFile: function()
408 this._fromFile = true;
411 __proto__: WebInspector.TargetAwareObject.prototype
416 * @implements {WebInspector.Searchable}
417 * @implements {WebInspector.ProfileType.DataDisplayDelegate}
418 * @extends {WebInspector.PanelWithSidebarTree}
420 WebInspector.ProfilesPanel = function()
422 WebInspector.PanelWithSidebarTree.call(this, "profiles");
423 this.registerRequiredCSS("panelEnablerView.css");
424 this.registerRequiredCSS("heapProfiler.css");
425 this.registerRequiredCSS("profilesPanel.css");
427 this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
428 this._target.profilingLock.addEventListener(WebInspector.Lock.Events.StateChanged, this._onProfilingStateChanged, this);
430 this._searchableView = new WebInspector.SearchableView(this);
432 var mainView = new WebInspector.VBox();
433 this._searchableView.show(mainView.element);
434 mainView.show(this.mainElement());
436 this.profilesItemTreeElement = new WebInspector.ProfilesSidebarTreeElement(this);
437 this.sidebarTree.appendChild(this.profilesItemTreeElement);
439 this.profileViews = document.createElement("div");
440 this.profileViews.id = "profile-views";
441 this.profileViews.classList.add("vbox");
442 this._searchableView.element.appendChild(this.profileViews);
444 var statusBarContainer = document.createElementWithClass("div", "profiles-status-bar");
445 mainView.element.insertBefore(statusBarContainer, mainView.element.firstChild);
446 this._statusBarElement = statusBarContainer.createChild("div", "status-bar");
448 this.sidebarElement().classList.add("profiles-sidebar-tree-box");
449 var statusBarContainerLeft = document.createElementWithClass("div", "profiles-status-bar");
450 this.sidebarElement().insertBefore(statusBarContainerLeft, this.sidebarElement().firstChild);
451 this._statusBarButtons = statusBarContainerLeft.createChild("div", "status-bar");
453 this.recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
454 this.recordButton.addEventListener("click", this.toggleRecordButton, this);
455 this._statusBarButtons.appendChild(this.recordButton.element);
457 this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");
458 this.clearResultsButton.addEventListener("click", this._reset, this);
459 this._statusBarButtons.appendChild(this.clearResultsButton.element);
461 this._profileTypeStatusBarItemsContainer = this._statusBarElement.createChild("div");
462 this._profileViewStatusBarItemsContainer = this._statusBarElement.createChild("div");
464 this._profileGroups = {};
465 this._launcherView = new WebInspector.MultiProfileLauncherView(this);
466 this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);
468 this._profileToView = [];
469 this._typeIdToSidebarSection = {};
470 var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
471 for (var i = 0; i < types.length; i++)
472 this._registerProfileType(types[i]);
473 this._launcherView.restoreSelectedProfileType();
474 this.profilesItemTreeElement.select();
475 this._showLauncherView();
477 this._createFileSelectorElement();
478 this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
479 this._registerShortcuts();
481 this._configureCpuProfilerSamplingInterval();
482 WebInspector.settings.highResolutionCpuProfiling.addChangeListener(this._configureCpuProfilerSamplingInterval, this);
489 WebInspector.ProfileTypeRegistry = function() {
490 this._profileTypes = [];
492 this.cpuProfileType = new WebInspector.CPUProfileType();
493 this._addProfileType(this.cpuProfileType);
494 this.heapSnapshotProfileType = new WebInspector.HeapSnapshotProfileType();
495 this._addProfileType(this.heapSnapshotProfileType);
496 this.trackingHeapSnapshotProfileType = new WebInspector.TrackingHeapSnapshotProfileType();
497 this._addProfileType(this.trackingHeapSnapshotProfileType);
498 HeapProfilerAgent.enable();
500 if (Capabilities.isMainFrontend && WebInspector.experimentsSettings.canvasInspection.isEnabled()) {
501 this.canvasProfileType = new WebInspector.CanvasProfileType();
502 this._addProfileType(this.canvasProfileType);
506 WebInspector.ProfileTypeRegistry.prototype = {
508 * @param {!WebInspector.ProfileType} profileType
510 _addProfileType: function(profileType)
512 this._profileTypes.push(profileType);
516 * @return {!Array.<!WebInspector.ProfileType>}
518 profileTypes: function()
520 return this._profileTypes;
526 WebInspector.ProfilesPanel.prototype = {
528 * @return {!WebInspector.SearchableView}
530 searchableView: function()
532 return this._searchableView;
535 _createFileSelectorElement: function()
537 if (this._fileSelectorElement)
538 this.element.removeChild(this._fileSelectorElement);
539 this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
540 this.element.appendChild(this._fileSelectorElement);
543 _findProfileTypeByExtension: function(fileName)
545 var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
546 for (var i = 0; i < types.length; i++) {
548 var extension = type.fileExtension();
551 if (fileName.endsWith(type.fileExtension()))
557 _registerShortcuts: function()
559 this.registerShortcuts(WebInspector.ShortcutsScreen.ProfilesPanelShortcuts.StartStopRecording, this.toggleRecordButton.bind(this));
562 _configureCpuProfilerSamplingInterval: function()
564 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get() ? 100 : 1000;
565 ProfilerAgent.setSamplingInterval(intervalUs, didChangeInterval);
566 function didChangeInterval(error)
569 WebInspector.console.showErrorMessage(error);
574 * @param {!File} file
576 _loadFromFile: function(file)
578 this._createFileSelectorElement();
580 var profileType = this._findProfileTypeByExtension(file.name);
583 var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
584 for (var i = 0; i < types.length; i++) {
585 var extension = types[i].fileExtension();
588 extensions.push(extension);
590 WebInspector.console.log(WebInspector.UIString("Can't load file. Only files with extensions '%s' can be loaded.", extensions.join("', '")));
594 if (!!profileType.profileBeingRecorded()) {
595 WebInspector.console.log(WebInspector.UIString("Can't load profile when other profile is recording."));
599 profileType.loadFromFile(file);
605 toggleRecordButton: function()
607 if (!this.recordButton.enabled())
609 var type = this._selectedProfileType;
610 var isProfiling = type.buttonClicked();
611 this._updateRecordButton(isProfiling);
613 this._launcherView.profileStarted();
614 if (type.hasTemporaryView())
615 this.showProfile(type.profileBeingRecorded());
617 this._launcherView.profileFinished();
622 _onProfilingStateChanged: function()
624 this._updateRecordButton(this.recordButton.toggled);
628 * @param {boolean} toggled
630 _updateRecordButton: function(toggled)
632 var enable = toggled || !this._target.profilingLock.isAcquired();
633 this.recordButton.setEnabled(enable);
634 this.recordButton.toggled = toggled;
636 this.recordButton.title = this._selectedProfileType ? this._selectedProfileType.buttonTooltip : "";
638 this.recordButton.title = WebInspector.UIString("Another profiler is already active");
639 if (this._selectedProfileType)
640 this._launcherView.updateProfileType(this._selectedProfileType, enable);
643 _profileBeingRecordedRemoved: function()
645 this._updateRecordButton(false);
646 this._launcherView.profileFinished();
650 * @param {!WebInspector.Event} event
652 _onProfileTypeSelected: function(event)
654 this._selectedProfileType = /** @type {!WebInspector.ProfileType} */ (event.data);
655 this._updateProfileTypeSpecificUI();
658 _updateProfileTypeSpecificUI: function()
660 this._updateRecordButton(this.recordButton.toggled);
661 this._profileTypeStatusBarItemsContainer.removeChildren();
662 var statusBarItems = this._selectedProfileType.statusBarItems;
663 if (statusBarItems) {
664 for (var i = 0; i < statusBarItems.length; ++i)
665 this._profileTypeStatusBarItemsContainer.appendChild(statusBarItems[i]);
671 WebInspector.Panel.prototype.reset.call(this);
673 var types = WebInspector.ProfileTypeRegistry.instance.profileTypes();
674 for (var i = 0; i < types.length; i++)
677 delete this.visibleView;
678 delete this.currentQuery;
679 this.searchCanceled();
681 this._profileGroups = {};
682 this._updateRecordButton(false);
683 this._launcherView.profileFinished();
685 this.sidebarTree.element.classList.remove("some-expandable");
687 this._launcherView.detach();
688 this.profileViews.removeChildren();
689 this._profileViewStatusBarItemsContainer.removeChildren();
691 this.removeAllListeners();
693 this.recordButton.visible = true;
694 this._profileViewStatusBarItemsContainer.classList.remove("hidden");
695 this.clearResultsButton.element.classList.remove("hidden");
696 this.profilesItemTreeElement.select();
697 this._showLauncherView();
700 _showLauncherView: function()
702 this.closeVisibleView();
703 this._profileViewStatusBarItemsContainer.removeChildren();
704 this._launcherView.show(this.profileViews);
705 this.visibleView = this._launcherView;
708 _garbageCollectButtonClicked: function()
710 HeapProfilerAgent.collectGarbage();
714 * @param {!WebInspector.ProfileType} profileType
716 _registerProfileType: function(profileType)
718 this._launcherView.addProfileType(profileType);
719 var profileTypeSection = new WebInspector.ProfileTypeSidebarSection(this, profileType);
720 this._typeIdToSidebarSection[profileType.id] = profileTypeSection
721 this.sidebarTree.appendChild(profileTypeSection);
722 profileTypeSection.childrenListElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
725 * @param {!WebInspector.Event} event
726 * @this {WebInspector.ProfilesPanel}
728 function onAddProfileHeader(event)
730 this._addProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
734 * @param {!WebInspector.Event} event
735 * @this {WebInspector.ProfilesPanel}
737 function onRemoveProfileHeader(event)
739 this._removeProfileHeader(/** @type {!WebInspector.ProfileHeader} */ (event.data));
743 * @param {!WebInspector.Event} event
744 * @this {WebInspector.ProfilesPanel}
746 function profileComplete(event)
748 this.showProfile(/** @type {!WebInspector.ProfileHeader} */ (event.data));
751 profileType.addEventListener(WebInspector.ProfileType.Events.ViewUpdated, this._updateProfileTypeSpecificUI, this);
752 profileType.addEventListener(WebInspector.ProfileType.Events.AddProfileHeader, onAddProfileHeader, this);
753 profileType.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, onRemoveProfileHeader, this);
754 profileType.addEventListener(WebInspector.ProfileType.Events.ProfileComplete, profileComplete, this);
756 var profiles = profileType.getProfiles();
757 for (var i = 0; i < profiles.length; i++)
758 this._addProfileHeader(profiles[i]);
762 * @param {?Event} event
764 _handleContextMenuEvent: function(event)
766 var element = event.srcElement;
767 while (element && !element.treeElement && element !== this.element)
768 element = element.parentElement;
771 if (element.treeElement && element.treeElement.handleContextMenuEvent) {
772 element.treeElement.handleContextMenuEvent(event, this);
776 var contextMenu = new WebInspector.ContextMenu(event);
777 if (this.visibleView instanceof WebInspector.HeapSnapshotView) {
778 this.visibleView.populateContextMenu(contextMenu, event);
780 if (element !== this.element || event.srcElement === this.sidebarElement()) {
781 contextMenu.appendItem(WebInspector.UIString("Load\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
786 showLoadFromFileDialog: function()
788 this._fileSelectorElement.click();
792 * @param {!WebInspector.ProfileHeader} profile
794 _addProfileHeader: function(profile)
796 var profileType = profile.profileType();
797 var typeId = profileType.id;
798 this._typeIdToSidebarSection[typeId].addProfileHeader(profile);;
799 if (!this.visibleView || this.visibleView === this._launcherView)
800 this.showProfile(profile);
804 * @param {!WebInspector.ProfileHeader} profile
806 _removeProfileHeader: function(profile)
808 if (profile.profileType()._profileBeingRecorded === profile)
809 this._profileBeingRecordedRemoved();
811 var i = this._indexOfViewForProfile(profile);
813 this._profileToView.splice(i, 1);
815 var profileType = profile.profileType();
816 var typeId = profileType.id;
817 var sectionIsEmpty = this._typeIdToSidebarSection[typeId].removeProfileHeader(profile);
819 // No other item will be selected if there aren't any other profiles, so
820 // make sure that view gets cleared when the last profile is removed.
821 if (sectionIsEmpty) {
822 this.profilesItemTreeElement.select();
823 this._showLauncherView();
828 * @param {?WebInspector.ProfileHeader} profile
829 * @return {?WebInspector.View}
831 showProfile: function(profile)
833 if (!profile || (profile.profileType().profileBeingRecorded() === profile) && !profile.profileType().hasTemporaryView())
836 var view = this._viewForProfile(profile);
837 if (view === this.visibleView)
840 this.closeVisibleView();
842 view.show(this.profileViews);
844 this.visibleView = view;
846 var profileTypeSection = this._typeIdToSidebarSection[profile.profileType().id];
847 var sidebarElement = profileTypeSection.sidebarElementForProfile(profile);
848 sidebarElement.revealAndSelect();
850 this._profileViewStatusBarItemsContainer.removeChildren();
852 var statusBarItems = view.statusBarItems;
854 for (var i = 0; i < statusBarItems.length; ++i)
855 this._profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
861 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
862 * @param {string} perspectiveName
864 showObject: function(snapshotObjectId, perspectiveName)
866 var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
867 for (var i = 0; i < heapProfiles.length; i++) {
868 var profile = heapProfiles[i];
869 // FIXME: allow to choose snapshot if there are several options.
870 if (profile.maxJSObjectId >= snapshotObjectId) {
871 this.showProfile(profile);
872 var view = this._viewForProfile(profile);
873 view.highlightLiveObject(perspectiveName, snapshotObjectId);
880 * @param {!WebInspector.ProfileHeader} profile
881 * @return {!WebInspector.View}
883 _viewForProfile: function(profile)
885 var index = this._indexOfViewForProfile(profile);
887 return this._profileToView[index].view;
888 var view = profile.createView(this);
889 view.element.classList.add("profile-view");
890 this._profileToView.push({ profile: profile, view: view});
895 * @param {!WebInspector.ProfileHeader} profile
898 _indexOfViewForProfile: function(profile)
900 for (var i = 0; i < this._profileToView.length; i++) {
901 if (this._profileToView[i].profile === profile)
907 closeVisibleView: function()
909 if (this.visibleView)
910 this.visibleView.detach();
911 delete this.visibleView;
915 * @param {string} query
916 * @param {boolean} shouldJump
917 * @param {boolean=} jumpBackwards
919 performSearch: function(query, shouldJump, jumpBackwards)
921 this.searchCanceled();
923 var visibleView = this.visibleView;
928 * @this {WebInspector.ProfilesPanel}
930 function finishedCallback(view, searchMatches)
934 this._searchableView.updateSearchMatchesCount(searchMatches);
935 this._searchResultsView = view;
938 view.jumpToLastSearchResult();
940 view.jumpToFirstSearchResult();
941 this._searchableView.updateCurrentMatchIndex(view.currentSearchResultIndex());
945 visibleView.currentQuery = query;
946 visibleView.performSearch(query, finishedCallback.bind(this));
949 jumpToNextSearchResult: function()
951 if (!this._searchResultsView)
953 if (this._searchResultsView !== this.visibleView)
955 this._searchResultsView.jumpToNextSearchResult();
956 this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
959 jumpToPreviousSearchResult: function()
961 if (!this._searchResultsView)
963 if (this._searchResultsView !== this.visibleView)
965 this._searchResultsView.jumpToPreviousSearchResult();
966 this._searchableView.updateCurrentMatchIndex(this._searchResultsView.currentSearchResultIndex());
969 searchCanceled: function()
971 if (this._searchResultsView) {
972 if (this._searchResultsView.searchCanceled)
973 this._searchResultsView.searchCanceled();
974 this._searchResultsView.currentQuery = null;
975 this._searchResultsView = null;
977 this._searchableView.updateSearchMatchesCount(0);
981 * @param {!WebInspector.ContextMenu} contextMenu
982 * @param {!Object} target
984 appendApplicableItems: function(event, contextMenu, target)
986 if (!(target instanceof WebInspector.RemoteObject))
989 if (WebInspector.inspectorView.currentPanel() !== this)
992 var object = /** @type {!WebInspector.RemoteObject} */ (target);
993 var objectId = object.objectId;
997 var heapProfiles = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType.getProfiles();
998 if (!heapProfiles.length)
1002 * @this {WebInspector.ProfilesPanel}
1004 function revealInView(viewName)
1006 HeapProfilerAgent.getHeapObjectId(objectId, didReceiveHeapObjectId.bind(this, viewName));
1010 * @this {WebInspector.ProfilesPanel}
1012 function didReceiveHeapObjectId(viewName, error, result)
1014 if (WebInspector.inspectorView.currentPanel() !== this)
1017 this.showObject(result, viewName);
1020 if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
1021 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInView.bind(this, "Dominators"));
1022 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInView.bind(this, "Summary"));
1025 __proto__: WebInspector.PanelWithSidebarTree.prototype
1031 * @extends {WebInspector.SidebarSectionTreeElement}
1032 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1033 * @param {!WebInspector.ProfileType} profileType
1035 WebInspector.ProfileTypeSidebarSection = function(dataDisplayDelegate, profileType)
1037 WebInspector.SidebarSectionTreeElement.call(this, profileType.treeItemTitle, null, true);
1038 this._dataDisplayDelegate = dataDisplayDelegate;
1039 this._profileTreeElements = [];
1040 this._profileGroups = {};
1047 WebInspector.ProfileTypeSidebarSection.ProfileGroup = function()
1049 this.profileSidebarTreeElements = [];
1050 this.sidebarTreeElement = null;
1053 WebInspector.ProfileTypeSidebarSection.prototype = {
1055 * @param {!WebInspector.ProfileHeader} profile
1057 addProfileHeader: function(profile)
1059 this.hidden = false;
1060 var profileType = profile.profileType();
1061 var sidebarParent = this;
1062 var profileTreeElement = profile.createSidebarTreeElement(this._dataDisplayDelegate);
1063 this._profileTreeElements.push(profileTreeElement);
1065 if (!profile.fromFile() && profileType.profileBeingRecorded() !== profile) {
1066 var profileTitle = profile.title;
1067 var group = this._profileGroups[profileTitle];
1069 group = new WebInspector.ProfileTypeSidebarSection.ProfileGroup();
1070 this._profileGroups[profileTitle] = group;
1072 group.profileSidebarTreeElements.push(profileTreeElement);
1074 var groupSize = group.profileSidebarTreeElements.length;
1075 if (groupSize === 2) {
1076 // Make a group TreeElement now that there are 2 profiles.
1077 group.sidebarTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(this._dataDisplayDelegate, profile.title);
1079 var firstProfileTreeElement = group.profileSidebarTreeElements[0];
1080 // Insert at the same index for the first profile of the group.
1081 var index = this.children.indexOf(firstProfileTreeElement);
1082 this.insertChild(group.sidebarTreeElement, index);
1084 // Move the first profile to the group.
1085 var selected = firstProfileTreeElement.selected;
1086 this.removeChild(firstProfileTreeElement);
1087 group.sidebarTreeElement.appendChild(firstProfileTreeElement);
1089 firstProfileTreeElement.revealAndSelect();
1091 firstProfileTreeElement.small = true;
1092 firstProfileTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
1094 this.treeOutline.element.classList.add("some-expandable");
1097 if (groupSize >= 2) {
1098 sidebarParent = group.sidebarTreeElement;
1099 profileTreeElement.small = true;
1100 profileTreeElement.mainTitle = WebInspector.UIString("Run %d", groupSize);
1104 sidebarParent.appendChild(profileTreeElement);
1108 * @param {!WebInspector.ProfileHeader} profile
1111 removeProfileHeader: function(profile)
1113 var index = this._sidebarElementIndex(profile);
1116 var profileTreeElement = this._profileTreeElements[index];
1117 this._profileTreeElements.splice(index, 1);
1119 var sidebarParent = this;
1120 var group = this._profileGroups[profile.title];
1122 var groupElements = group.profileSidebarTreeElements;
1123 groupElements.splice(groupElements.indexOf(profileTreeElement), 1);
1124 if (groupElements.length === 1) {
1125 // Move the last profile out of its group and remove the group.
1126 var pos = sidebarParent.children.indexOf(group.sidebarTreeElement);
1127 this.insertChild(groupElements[0], pos);
1128 groupElements[0].small = false;
1129 groupElements[0].mainTitle = group.sidebarTreeElement.title;
1130 this.removeChild(group.sidebarTreeElement);
1132 if (groupElements.length !== 0)
1133 sidebarParent = group.sidebarTreeElement;
1135 sidebarParent.removeChild(profileTreeElement);
1136 profileTreeElement.dispose();
1138 if (this.children.length)
1145 * @param {!WebInspector.ProfileHeader} profile
1146 * @return {?WebInspector.ProfileSidebarTreeElement}
1148 sidebarElementForProfile: function(profile)
1150 var index = this._sidebarElementIndex(profile);
1151 return index === -1 ? null : this._profileTreeElements[index];
1155 * @param {!WebInspector.ProfileHeader} profile
1158 _sidebarElementIndex: function(profile)
1160 var elements = this._profileTreeElements;
1161 for (var i = 0; i < elements.length; i++) {
1162 if (elements[i].profile === profile)
1168 __proto__: WebInspector.SidebarSectionTreeElement.prototype
1174 * @implements {WebInspector.ContextMenu.Provider}
1176 WebInspector.ProfilesPanel.ContextMenuProvider = function()
1180 WebInspector.ProfilesPanel.ContextMenuProvider.prototype = {
1182 * @param {!WebInspector.ContextMenu} contextMenu
1183 * @param {!Object} target
1185 appendApplicableItems: function(event, contextMenu, target)
1187 WebInspector.inspectorView.panel("profiles").appendApplicableItems(event, contextMenu, target);
1193 * @extends {WebInspector.SidebarTreeElement}
1194 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1195 * @param {!WebInspector.ProfileHeader} profile
1196 * @param {string} className
1198 WebInspector.ProfileSidebarTreeElement = function(dataDisplayDelegate, profile, className)
1200 this._dataDisplayDelegate = dataDisplayDelegate;
1201 this.profile = profile;
1202 WebInspector.SidebarTreeElement.call(this, className, profile.title, "", profile, false);
1203 this.refreshTitles();
1204 profile.addEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
1205 if (profile.canSaveToFile())
1206 this._createSaveLink();
1208 profile.addEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
1211 WebInspector.ProfileSidebarTreeElement.prototype = {
1212 _createSaveLink: function()
1214 this._saveLinkElement = this.titleContainer.createChild("span", "save-link");
1215 this._saveLinkElement.textContent = WebInspector.UIString("Save");
1216 this._saveLinkElement.addEventListener("click", this._saveProfile.bind(this), false);
1219 _onProfileReceived: function(event)
1221 this._createSaveLink();
1225 * @param {!WebInspector.Event} event
1227 _updateStatus: function(event)
1229 var statusUpdate = event.data;
1230 if (statusUpdate.subtitle !== null)
1231 this.subtitle = statusUpdate.subtitle;
1232 if (typeof statusUpdate.wait === "boolean")
1233 this.wait = statusUpdate.wait;
1234 this.refreshTitles();
1239 this.profile.removeEventListener(WebInspector.ProfileHeader.Events.UpdateStatus, this._updateStatus, this);
1240 this.profile.removeEventListener(WebInspector.ProfileHeader.Events.ProfileReceived, this._onProfileReceived, this);
1243 onselect: function()
1245 this._dataDisplayDelegate.showProfile(this.profile);
1251 ondelete: function()
1253 this.profile.profileType().removeProfile(this.profile);
1258 * @param {!Event} event
1259 * @param {!WebInspector.ProfilesPanel} panel
1261 handleContextMenuEvent: function(event, panel)
1263 var profile = this.profile;
1264 var contextMenu = new WebInspector.ContextMenu(event);
1265 // FIXME: use context menu provider
1266 contextMenu.appendItem(WebInspector.UIString("Load\u2026"), panel._fileSelectorElement.click.bind(panel._fileSelectorElement));
1267 if (profile.canSaveToFile())
1268 contextMenu.appendItem(WebInspector.UIString("Save\u2026"), profile.saveToFile.bind(profile));
1269 contextMenu.appendItem(WebInspector.UIString("Delete"), this.ondelete.bind(this));
1273 _saveProfile: function(event)
1275 this.profile.saveToFile();
1278 __proto__: WebInspector.SidebarTreeElement.prototype
1283 * @extends {WebInspector.SidebarTreeElement}
1284 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1285 * @param {string} title
1286 * @param {string=} subtitle
1288 WebInspector.ProfileGroupSidebarTreeElement = function(dataDisplayDelegate, title, subtitle)
1290 WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
1291 this._dataDisplayDelegate = dataDisplayDelegate;
1294 WebInspector.ProfileGroupSidebarTreeElement.prototype = {
1295 onselect: function()
1297 if (this.children.length > 0)
1298 this._dataDisplayDelegate.showProfile(this.children[this.children.length - 1].profile);
1301 __proto__: WebInspector.SidebarTreeElement.prototype
1306 * @extends {WebInspector.SidebarTreeElement}
1307 * @param {!WebInspector.ProfilesPanel} panel
1309 WebInspector.ProfilesSidebarTreeElement = function(panel)
1311 this._panel = panel;
1314 WebInspector.SidebarTreeElement.call(this, "profile-launcher-view-tree-item", WebInspector.UIString("Profiles"), "", null, false);
1317 WebInspector.ProfilesSidebarTreeElement.prototype = {
1318 onselect: function()
1320 this._panel._showLauncherView();
1328 __proto__: WebInspector.SidebarTreeElement.prototype
1332 importScript("../components/CPUProfileModel.js");
1333 importScript("CPUProfileDataGrid.js");
1334 importScript("CPUProfileBottomUpDataGrid.js");
1335 importScript("CPUProfileTopDownDataGrid.js");
1336 importScript("CPUProfileFlameChart.js");
1337 importScript("CPUProfileView.js");
1338 importScript("HeapSnapshotCommon.js");
1339 importScript("HeapSnapshotProxy.js");
1340 importScript("HeapSnapshotDataGrids.js");
1341 importScript("HeapSnapshotGridNodes.js");
1342 importScript("HeapSnapshotView.js");
1343 importScript("ProfileLauncherView.js");
1344 importScript("CanvasProfileView.js");
1345 importScript("CanvasReplayStateView.js");
1347 WebInspector.ProfileTypeRegistry.instance = new WebInspector.ProfileTypeRegistry();