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
;
73 // Must be implemented by subclasses.
74 createView: function(profile
)
76 throw new Error("Needs implemented.");
79 // Must be implemented by subclasses.
80 createSidebarTreeElementForProfile: function(profile
)
82 throw new Error("Needs implemented.");
86 WebInspector
.ProfilesPanel = function()
88 WebInspector
.Panel
.call(this);
92 this.element
.addStyleClass("profiles");
93 this._profileTypesByIdMap
= {};
94 this._profileTypeButtonsByIdMap
= {};
96 var panelEnablerHeading
= WebInspector
.UIString("You need to enable profiling before you can use the Profiles panel.");
97 var panelEnablerDisclaimer
= WebInspector
.UIString("Enabling profiling will make scripts run slower.");
98 var panelEnablerButton
= WebInspector
.UIString("Enable Profiling");
99 this.panelEnablerView
= new WebInspector
.PanelEnablerView("profiles", panelEnablerHeading
, panelEnablerDisclaimer
, panelEnablerButton
);
100 this.panelEnablerView
.addEventListener("enable clicked", this._enableProfiling
, this);
102 this.element
.appendChild(this.panelEnablerView
.element
);
104 this.profileViews
= document
.createElement("div");
105 this.profileViews
.id
= "profile-views";
106 this.element
.appendChild(this.profileViews
);
108 this.enableToggleButton
= new WebInspector
.StatusBarButton("", "enable-toggle-status-bar-item");
109 this.enableToggleButton
.addEventListener("click", this._toggleProfiling
.bind(this), false);
111 this.profileViewStatusBarItemsContainer
= document
.createElement("div");
112 this.profileViewStatusBarItemsContainer
.id
= "profile-view-status-bar-items";
118 WebInspector
.ProfilesPanel
.prototype = {
119 toolbarItemClass
: "profiles",
121 get toolbarItemLabel()
123 return WebInspector
.UIString("Profiles");
128 function clickHandler(profileType
, buttonElement
)
130 profileType
.buttonClicked
.call(profileType
);
131 this.updateProfileTypeButtons();
134 var items
= [this.enableToggleButton
.element
];
135 // FIXME: Generate a single "combo-button".
136 for (var typeId
in this._profileTypesByIdMap
) {
137 var profileType
= this.getProfileType(typeId
);
138 if (profileType
.buttonStyle
) {
139 var button
= new WebInspector
.StatusBarButton(profileType
.buttonTooltip
, profileType
.buttonStyle
, profileType
.buttonCaption
);
140 this._profileTypeButtonsByIdMap
[typeId
] = button
.element
;
141 button
.element
.addEventListener("click", clickHandler
.bind(this, profileType
, button
.element
), false);
142 items
.push(button
.element
);
145 items
.push(this.profileViewStatusBarItemsContainer
);
151 WebInspector
.Panel
.prototype.show
.call(this);
152 if (this._shouldPopulateProfiles
)
153 this._populateProfiles();
156 populateInterface: function()
159 this._populateProfiles();
161 this._shouldPopulateProfiles
= true;
164 profilerWasEnabled: function()
167 this.populateInterface();
170 profilerWasDisabled: function()
177 for (var i
= 0; i
< this._profiles
.length
; ++i
)
178 delete this._profiles
[i
]._profileView
;
180 delete this.currentQuery
;
181 this.searchCanceled();
184 this._profilesIdMap
= {};
185 this._profileGroups
= {};
186 this._profileGroupsForLinks
= {}
188 this.sidebarTreeElement
.removeStyleClass("some-expandable");
190 for (var typeId
in this._profileTypesByIdMap
)
191 this.getProfileType(typeId
).treeElement
.removeChildren();
193 this.profileViews
.removeChildren();
195 this.profileViewStatusBarItemsContainer
.removeChildren();
197 this._updateInterface();
200 registerProfileType: function(profileType
)
202 this._profileTypesByIdMap
[profileType
.id
] = profileType
;
203 profileType
.treeElement
= new WebInspector
.SidebarSectionTreeElement(profileType
.name
, null, true);
204 this.sidebarTree
.appendChild(profileType
.treeElement
);
205 profileType
.treeElement
.expand();
208 _makeKey: function(text
, profileTypeId
)
210 return escape(text
) + '/' + escape(profileTypeId
);
213 addProfileHeader: function(profile
)
215 var typeId
= profile
.typeId
;
216 var profileType
= this.getProfileType(typeId
);
217 var sidebarParent
= profileType
.treeElement
;
221 profile
.__profilesPanelProfileType
= profileType
;
222 this._profiles
.push(profile
);
223 this._profilesIdMap
[this._makeKey(profile
.uid
, typeId
)] = profile
;
225 if (profile
.title
.indexOf(UserInitiatedProfileName
) !== 0) {
226 var profileTitleKey
= this._makeKey(profile
.title
, typeId
);
227 if (!(profileTitleKey
in this._profileGroups
))
228 this._profileGroups
[profileTitleKey
] = [];
230 var group
= this._profileGroups
[profileTitleKey
];
233 if (group
.length
=== 2) {
234 // Make a group TreeElement now that there are 2 profiles.
235 group
._profilesTreeElement
= new WebInspector
.ProfileGroupSidebarTreeElement(profile
.title
);
237 // Insert at the same index for the first profile of the group.
238 var index
= sidebarParent
.children
.indexOf(group
[0]._profilesTreeElement
);
239 sidebarParent
.insertChild(group
._profilesTreeElement
, index
);
241 // Move the first profile to the group.
242 var selected
= group
[0]._profilesTreeElement
.selected
;
243 sidebarParent
.removeChild(group
[0]._profilesTreeElement
);
244 group
._profilesTreeElement
.appendChild(group
[0]._profilesTreeElement
);
246 group
[0]._profilesTreeElement
.select();
247 group
[0]._profilesTreeElement
.reveal();
250 group
[0]._profilesTreeElement
.small
= true;
251 group
[0]._profilesTreeElement
.mainTitle
= WebInspector
.UIString("Run %d", 1);
253 this.sidebarTreeElement
.addStyleClass("some-expandable");
256 if (group
.length
>= 2) {
257 sidebarParent
= group
._profilesTreeElement
;
258 alternateTitle
= WebInspector
.UIString("Run %d", group
.length
);
263 var profileTreeElement
= profileType
.createSidebarTreeElementForProfile(profile
);
264 profileTreeElement
.small
= small
;
266 profileTreeElement
.mainTitle
= alternateTitle
;
267 profile
._profilesTreeElement
= profileTreeElement
;
269 sidebarParent
.appendChild(profileTreeElement
);
270 if (!this.visibleView
)
271 this.showProfile(profile
);
274 showProfile: function(profile
)
279 if (this.visibleView
)
280 this.visibleView
.hide();
282 var view
= profile
.__profilesPanelProfileType
.viewForProfile(profile
);
284 view
.show(this.profileViews
);
286 profile
._profilesTreeElement
.select(true);
287 profile
._profilesTreeElement
.reveal();
289 this.visibleView
= view
;
291 this.profileViewStatusBarItemsContainer
.removeChildren();
293 var statusBarItems
= view
.statusBarItems
;
294 for (var i
= 0; i
< statusBarItems
.length
; ++i
)
295 this.profileViewStatusBarItemsContainer
.appendChild(statusBarItems
[i
]);
298 showView: function(view
)
300 this.showProfile(view
.profile
);
303 getProfileType: function(typeId
)
305 return this._profileTypesByIdMap
[typeId
];
308 showProfileForURL: function(url
)
310 var match
= url
.match(WebInspector
.ProfileType
.URLRegExp
);
313 this.showProfile(this._profilesIdMap
[this._makeKey(match
[3], match
[1])]);
316 updateProfileTypeButtons: function()
318 for (var typeId
in this._profileTypeButtonsByIdMap
) {
319 var buttonElement
= this._profileTypeButtonsByIdMap
[typeId
];
320 var profileType
= this.getProfileType(typeId
);
321 buttonElement
.className
= profileType
.buttonStyle
;
322 buttonElement
.title
= profileType
.buttonTooltip
;
323 // FIXME: Apply profileType.buttonCaption once captions are added to button controls.
327 closeVisibleView: function()
329 if (this.visibleView
)
330 this.visibleView
.hide();
331 delete this.visibleView
;
334 displayTitleForProfileLink: function(title
, typeId
)
336 title
= unescape(title
);
337 if (title
.indexOf(UserInitiatedProfileName
) === 0) {
338 title
= WebInspector
.UIString("Profile %d", title
.substring(UserInitiatedProfileName
.length
+ 1));
340 var titleKey
= this._makeKey(title
, typeId
);
341 if (!(titleKey
in this._profileGroupsForLinks
))
342 this._profileGroupsForLinks
[titleKey
] = 0;
344 groupNumber
= ++this._profileGroupsForLinks
[titleKey
];
347 // The title is used in the console message announcing that a profile has started so it gets
348 // incremented twice as often as it's displayed
349 title
+= " " + WebInspector
.UIString("Run %d", groupNumber
/ 2);
355 get searchableViews()
359 const visibleView
= this.visibleView
;
360 if (visibleView
&& visibleView
.performSearch
)
361 views
.push(visibleView
);
363 var profilesLength
= this._profiles
.length
;
364 for (var i
= 0; i
< profilesLength
; ++i
) {
365 var profile
= this._profiles
[i
];
366 var view
= profile
.__profilesPanelProfileType
.viewForProfile(profile
);
367 if (!view
.performSearch
|| view
=== visibleView
)
375 searchMatchFound: function(view
, matches
)
377 view
.profile
._profilesTreeElement
.searchMatches
= matches
;
380 searchCanceled: function(startingNewSearch
)
382 WebInspector
.Panel
.prototype.searchCanceled
.call(this, startingNewSearch
);
387 for (var i
= 0; i
< this._profiles
.length
; ++i
) {
388 var profile
= this._profiles
[i
];
389 profile
._profilesTreeElement
.searchMatches
= 0;
395 var visibleView
= this.visibleView
;
396 if (visibleView
&& "resize" in visibleView
)
397 visibleView
.resize();
400 _updateInterface: function()
402 // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change.
403 if (InspectorBackend
.profilerEnabled()) {
404 this.enableToggleButton
.title
= WebInspector
.UIString("Profiling enabled. Click to disable.");
405 this.enableToggleButton
.toggled
= true;
406 for (var typeId
in this._profileTypeButtonsByIdMap
)
407 this._profileTypeButtonsByIdMap
[typeId
].removeStyleClass("hidden");
408 this.profileViewStatusBarItemsContainer
.removeStyleClass("hidden");
409 this.panelEnablerView
.visible
= false;
411 this.enableToggleButton
.title
= WebInspector
.UIString("Profiling disabled. Click to enable.");
412 this.enableToggleButton
.toggled
= false;
413 for (var typeId
in this._profileTypeButtonsByIdMap
)
414 this._profileTypeButtonsByIdMap
[typeId
].addStyleClass("hidden");
415 this.profileViewStatusBarItemsContainer
.addStyleClass("hidden");
416 this.panelEnablerView
.visible
= true;
420 _enableProfiling: function()
422 if (InspectorBackend
.profilerEnabled())
424 this._toggleProfiling(this.panelEnablerView
.alwaysEnabled
);
427 _toggleProfiling: function(optionalAlways
)
429 if (InspectorBackend
.profilerEnabled())
430 InspectorBackend
.disableProfiler(true);
432 InspectorBackend
.enableProfiler(!!optionalAlways
);
435 _populateProfiles: function()
437 var sidebarTreeChildrenCount
= this.sidebarTree
.children
.length
;
438 for (var i
= 0; i
< sidebarTreeChildrenCount
; ++i
) {
439 var treeElement
= this.sidebarTree
.children
[i
];
440 if (treeElement
.children
.length
)
444 function populateCallback(profileHeaders
) {
445 profileHeaders
.sort(function(a
, b
) { return a
.uid
- b
.uid
; });
446 var profileHeadersLength
= profileHeaders
.length
;
447 for (var i
= 0; i
< profileHeadersLength
; ++i
)
448 WebInspector
.addProfileHeader(profileHeaders
[i
]);
451 var callId
= WebInspector
.Callback
.wrap(populateCallback
);
452 InspectorBackend
.getProfileHeaders(callId
);
454 delete this._shouldPopulateProfiles
;
457 updateMainViewWidth: function(width
)
459 this.profileViews
.style
.left
= width
+ "px";
460 this.profileViewStatusBarItemsContainer
.style
.left
= width
+ "px";
464 WebInspector
.ProfilesPanel
.prototype.__proto__
= WebInspector
.Panel
.prototype;
466 WebInspector
.ProfileSidebarTreeElement = function(profile
)
468 this.profile
= profile
;
470 if (this.profile
.title
.indexOf(UserInitiatedProfileName
) === 0)
471 this._profileNumber
= this.profile
.title
.substring(UserInitiatedProfileName
.length
+ 1);
473 WebInspector
.SidebarTreeElement
.call(this, "profile-sidebar-tree-item", "", "", profile
, false);
475 this.refreshTitles();
478 WebInspector
.ProfileSidebarTreeElement
.prototype = {
481 WebInspector
.panels
.profiles
.showProfile(this.profile
);
487 return this._mainTitle
;
488 if (this.profile
.title
.indexOf(UserInitiatedProfileName
) === 0)
489 return WebInspector
.UIString("Profile %d", this._profileNumber
);
490 return this.profile
.title
;
496 this.refreshTitles();
501 // There is no subtitle.
506 // Can't change subtitle.
509 set searchMatches(matches
)
512 if (!this.bubbleElement
)
514 this.bubbleElement
.removeStyleClass("search-matches");
515 this.bubbleText
= "";
519 this.bubbleText
= matches
;
520 this.bubbleElement
.addStyleClass("search-matches");
524 WebInspector
.ProfileSidebarTreeElement
.prototype.__proto__
= WebInspector
.SidebarTreeElement
.prototype;
526 WebInspector
.ProfileGroupSidebarTreeElement = function(title
, subtitle
)
528 WebInspector
.SidebarTreeElement
.call(this, "profile-group-sidebar-tree-item", title
, subtitle
, null, true);
531 WebInspector
.ProfileGroupSidebarTreeElement
.prototype = {
534 WebInspector
.panels
.profiles
.showProfile(this.children
[this.children
.length
- 1].profile
);
538 WebInspector
.ProfileGroupSidebarTreeElement
.prototype.__proto__
= WebInspector
.SidebarTreeElement
.prototype;
540 WebInspector
.didGetProfileHeaders
= WebInspector
.Callback
.processCallback
;
541 WebInspector
.didGetProfile
= WebInspector
.Callback
.processCallback
;