tizen beta release
[profile/ivi/webkit-efl.git] / debian / tmp / usr / share / ewebkit-0 / webinspector / ProfilesPanel.js
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
27
28 WebInspector.ProfileType = function(id, name)
29 {
30     this._id = id;
31     this._name = name;
32 }
33
34 WebInspector.ProfileType.URLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/;
35
36 WebInspector.ProfileType.prototype = {
37     get buttonTooltip()
38     {
39         return "";
40     },
41
42     get buttonStyle()
43     {
44         return undefined;
45     },
46
47     get buttonCaption()
48     {
49         return this.name;
50     },
51
52     get id()
53     {
54         return this._id;
55     },
56
57     get name()
58     {
59         return this._name;
60     },
61
62     buttonClicked: function()
63     {
64     },
65
66     viewForProfile: function(profile)
67     {
68         if (!profile._profileView)
69             profile._profileView = this.createView(profile);
70         return profile._profileView;
71     },
72
73     get welcomeMessage()
74     {
75         return "";
76     },
77
78     // Must be implemented by subclasses.
79     createView: function(profile)
80     {
81         throw new Error("Needs implemented.");
82     },
83
84     // Must be implemented by subclasses.
85     createSidebarTreeElementForProfile: function(profile)
86     {
87         throw new Error("Needs implemented.");
88     }
89 }
90
91 WebInspector.registerLinkifierPlugin(function(title)
92 {
93     var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title);
94     if (profileStringMatches)
95         title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
96     return title;
97 });
98
99 WebInspector.ProfilesPanel = function()
100 {
101     WebInspector.Panel.call(this, "profiles");
102     this.registerRequiredCSS("heapProfiler.css");
103     this.registerRequiredCSS("profilesPanel.css");
104
105     this.createSplitViewWithSidebarTree();
106
107     this._profileTypesByIdMap = {};
108     this._profileTypeButtonsByIdMap = {};
109
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);
115
116     this.profileViews = document.createElement("div");
117     this.profileViews.id = "profile-views";
118     this.splitView.mainElement.appendChild(this.profileViews);
119
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");
124
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);
127
128     this.profileViewStatusBarItemsContainer = document.createElement("div");
129     this.profileViewStatusBarItemsContainer.className = "status-bar-items";
130
131     this.welcomeView = new WebInspector.WelcomeView("profiles", WebInspector.UIString("Welcome to the Profiles panel"));
132
133     this._profiles = [];
134     this._profilerEnabled = Capabilities.profilerAlwaysEnabled;
135     this._reset();
136
137     this._registerProfileType(new WebInspector.CPUProfileType());
138     if (Capabilities.heapProfilerPresent)
139         this._registerProfileType(new WebInspector.DetailedHeapshotProfileType());
140
141     InspectorBackend.registerProfilerDispatcher(new WebInspector.ProfilerDispatcher(this));
142
143     if (Capabilities.profilerAlwaysEnabled || WebInspector.settings.profilerEnabled.get())
144         ProfilerAgent.enable();
145     else {
146         function onProfilerEnebled(error, value) {
147             if (value)
148                 this._profilerWasEnabled();
149         }
150         ProfilerAgent.isEnabled(onProfilerEnebled.bind(this));
151     }
152 }
153
154 WebInspector.ProfilesPanel.prototype = {
155     get toolbarItemLabel()
156     {
157         return WebInspector.UIString("Profiles");
158     },
159
160     get statusBarItems()
161     {
162         function clickHandler(profileType, buttonElement)
163         {
164             profileType.buttonClicked.call(profileType);
165             this.updateProfileTypeButtons();
166         }
167
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);
177             }
178         }
179         items.push(this.clearResultsButton.element, this.profileViewStatusBarItemsContainer);
180         return items;
181     },
182
183     wasShown: function()
184     {
185         WebInspector.Panel.prototype.wasShown.call(this);
186         this._populateProfiles();
187     },
188
189     _profilerWasEnabled: function()
190     {
191         if (this._profilerEnabled)
192             return;
193
194         this._profilerEnabled = true;
195
196         this._reset();
197         if (this.isShowing())
198             this._populateProfiles();
199     },
200
201     _profilerWasDisabled: function()
202     {
203         if (!this._profilerEnabled)
204             return;
205
206         this._profilerEnabled = false;
207         this._reset();
208     },
209
210     _reset: function()
211     {
212         WebInspector.Panel.prototype.reset.call(this);
213
214         for (var i = 0; i < this._profiles.length; ++i) {
215             var view = this._profiles[i]._profileView;
216             if (view) {
217                 view.detach();
218                 if ("dispose" in view)
219                     view.dispose();
220             }
221
222             delete this._profiles[i]._profileView;
223         }
224         delete this.visibleView;
225
226         delete this.currentQuery;
227         this.searchCanceled();
228
229         this._profiles = [];
230         this._profilesIdMap = {};
231         this._profileGroups = {};
232         this._profileGroupsForLinks = {};
233         this._profilesWereRequested = false;
234
235         this.sidebarTreeElement.removeStyleClass("some-expandable");
236
237         for (var typeId in this._profileTypesByIdMap)
238             this.getProfileType(typeId).treeElement.removeChildren();
239
240         this.profileViews.removeChildren();
241
242         this.profileViewStatusBarItemsContainer.removeChildren();
243
244         this.removeAllListeners();
245
246         this._updateInterface();
247         this.welcomeView.show(this.splitView.mainElement);
248     },
249
250     _clearProfiles: function()
251     {
252         ProfilerAgent.clearProfiles();
253         this._reset();
254     },
255
256     _registerProfileType: function(profileType)
257     {
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);
263     },
264
265     _addWelcomeMessage: function(profileType)
266     {
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);
277
278             var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption);
279             container.appendChild(button.element);
280
281             var part2 = document.createElement("span");
282             part2.textContent = message.substr(buttonPos + 2);
283             container.appendChild(part2);
284             this.welcomeView.addMessage(container);
285         } else
286             this.welcomeView.addMessage(message);
287     },
288
289     _makeKey: function(text, profileTypeId)
290     {
291         return escape(text) + '/' + escape(profileTypeId);
292     },
293
294     _addProfileHeader: function(profile)
295     {
296         if (this.hasTemporaryProfile(profile.typeId)) {
297             if (profile.typeId === WebInspector.CPUProfileType.TypeId)
298                 this._removeProfileHeader(this._temporaryRecordingProfile);
299             else
300                 this._removeProfileHeader(this._temporaryTakingSnapshot);
301         }
302
303         var typeId = profile.typeId;
304         var profileType = this.getProfileType(typeId);
305         var sidebarParent = profileType.treeElement;
306         var small = false;
307         var alternateTitle;
308
309         profile.__profilesPanelProfileType = profileType;
310         this._profiles.push(profile);
311         this._profilesIdMap[this._makeKey(profile.uid, typeId)] = profile;
312
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] = [];
317
318             var group = this._profileGroups[profileTitleKey];
319             group.push(profile);
320
321             if (group.length === 2) {
322                 // Make a group TreeElement now that there are 2 profiles.
323                 group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title);
324
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);
328
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);
333                 if (selected)
334                     group[0]._profilesTreeElement.revealAndSelect();
335
336                 group[0]._profilesTreeElement.small = true;
337                 group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
338
339                 this.sidebarTreeElement.addStyleClass("some-expandable");
340             }
341
342             if (group.length >= 2) {
343                 sidebarParent = group._profilesTreeElement;
344                 alternateTitle = WebInspector.UIString("Run %d", group.length);
345                 small = true;
346             }
347         }
348
349         var profileTreeElement = profileType.createSidebarTreeElementForProfile(profile);
350         profile.sidebarElement = profileTreeElement;
351         profileTreeElement.small = small;
352         if (alternateTitle)
353             profileTreeElement.mainTitle = alternateTitle;
354         profile._profilesTreeElement = profileTreeElement;
355
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");
362         }
363     },
364
365     _removeProfileHeader: function(profile)
366     {
367         var typeId = profile.typeId;
368         var profileType = this.getProfileType(typeId);
369         var sidebarParent = profileType.treeElement;
370
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);
375                 break;
376             }
377         }
378         delete this._profilesIdMap[this._makeKey(profile.uid, typeId)];
379
380         var profileTitleKey = this._makeKey(profile.title, typeId);
381         delete this._profileGroups[profileTitleKey];
382
383         sidebarParent.removeChild(profile._profilesTreeElement);
384
385         if (!profile.isTemporary)
386             ProfilerAgent.removeProfile(profile.typeId, profile.uid);
387
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();
392     },
393
394     showProfile: function(profile)
395     {
396         if (!profile || profile.isTemporary)
397             return;
398
399         this.closeVisibleView();
400
401         var view = profile.__profilesPanelProfileType.viewForProfile(profile);
402
403         view.show(this.profileViews);
404
405         profile._profilesTreeElement._suppressOnSelect = true;
406         profile._profilesTreeElement.revealAndSelect();
407         delete profile._profilesTreeElement._suppressOnSelect;
408
409         this.visibleView = view;
410
411         this.profileViewStatusBarItemsContainer.removeChildren();
412
413         var statusBarItems = view.statusBarItems;
414         if (statusBarItems)
415             for (var i = 0; i < statusBarItems.length; ++i)
416                 this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
417     },
418
419     getProfiles: function(typeId)
420     {
421         var result = [];
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);
427         }
428         return result;
429     },
430
431     hasTemporaryProfile: function(typeId)
432     {
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)
436                 return true;
437         return false;
438     },
439
440     hasProfile: function(profile)
441     {
442         return !!this._profilesIdMap[this._makeKey(profile.uid, profile.typeId)];
443     },
444
445     getProfile: function(typeId, uid)
446     {
447         return this._profilesIdMap[this._makeKey(uid, typeId)];
448     },
449
450     loadHeapSnapshot: function(uid, callback)
451     {
452         var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
453         if (!profile)
454             return;
455
456         if (!profile.proxy) {
457             function setProfileWait(event) {
458                 profile.sidebarElement.wait = event.data;
459             }
460             var worker = new WebInspector.HeapSnapshotWorker();
461             worker.addEventListener("wait", setProfileWait, this);
462             profile.proxy = worker.createObject("WebInspector.HeapSnapshotLoader");
463         }
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);
469         }
470     },
471
472     _addHeapSnapshotChunk: function(uid, chunk)
473     {
474         var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
475         if (!profile || !profile.proxy)
476             return;
477         profile.proxy.pushJSONChunk(chunk);
478     },
479
480     _finishHeapSnapshot: function(uid)
481     {
482         var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.DetailedHeapshotProfileType.TypeId)];
483         if (!profile || !profile.proxy)
484             return;
485         var proxy = profile.proxy;
486         function parsed(snapshotProxy)
487         {
488             profile.proxy = snapshotProxy;
489             profile.sidebarElement.subtitle = Number.bytesToString(snapshotProxy.totalSize);
490             profile.sidebarElement.wait = false;
491             snapshotProxy.worker.startCheckingForLongRunningCalls();
492         }
493         if (proxy.finishLoading(parsed))
494             profile.sidebarElement.subtitle = WebInspector.UIString("Parsing\u2026");
495     },
496
497     showView: function(view)
498     {
499         this.showProfile(view.profile);
500     },
501
502     getProfileType: function(typeId)
503     {
504         return this._profileTypesByIdMap[typeId];
505     },
506
507     showProfileForURL: function(url)
508     {
509         var match = url.match(WebInspector.ProfileType.URLRegExp);
510         if (!match)
511             return;
512         this.showProfile(this._profilesIdMap[this._makeKey(match[3], match[1])]);
513     },
514
515     updateProfileTypeButtons: function()
516     {
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.
523         }
524     },
525
526     closeVisibleView: function()
527     {
528         if (this.visibleView)
529             this.visibleView.detach();
530         delete this.visibleView;
531     },
532
533     displayTitleForProfileLink: function(title, typeId)
534     {
535         title = unescape(title);
536         if (title.indexOf(UserInitiatedProfileName) === 0) {
537             title = WebInspector.UIString("Profile %d", title.substring(UserInitiatedProfileName.length + 1));
538         } else {
539             var titleKey = this._makeKey(title, typeId);
540             if (!(titleKey in this._profileGroupsForLinks))
541                 this._profileGroupsForLinks[titleKey] = 0;
542
543             var groupNumber = ++this._profileGroupsForLinks[titleKey];
544
545             if (groupNumber > 2)
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);
549         }
550
551         return title;
552     },
553
554     performSearch: function(query)
555     {
556         this.searchCanceled();
557
558         var searchableViews = this._searchableViews();
559         if (!searchableViews || !searchableViews.length)
560             return;
561
562         var parentElement = this.viewsContainerElement;
563         var visibleView = this.visibleView;
564         var sortFuction = this.searchResultsSortFunction;
565
566         var matchesCountUpdateTimeout = null;
567
568         function updateMatchesCount()
569         {
570             WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this);
571             matchesCountUpdateTimeout = null;
572         }
573
574         function updateMatchesCountSoon()
575         {
576             if (matchesCountUpdateTimeout)
577                 return;
578             // Update the matches count every half-second so it doesn't feel twitchy.
579             matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
580         }
581
582         function finishedCallback(view, searchMatches)
583         {
584             if (!searchMatches)
585                 return;
586
587             this._totalSearchMatches += searchMatches;
588             this._searchResults.push(view);
589
590             if (sortFuction)
591                 this._searchResults.sort(sortFuction);
592
593             if (this.searchMatchFound)
594                 this.searchMatchFound(view, searchMatches);
595
596             updateMatchesCountSoon.call(this);
597
598             if (view === visibleView)
599                 view.jumpToFirstSearchResult();
600         }
601
602         var i = 0;
603         var panel = this;
604         var boundFinishedCallback = finishedCallback.bind(this);
605         var chunkIntervalIdentifier = null;
606
607         // Split up the work into chunks so we don't block the
608         // UI thread while processing.
609
610         function processChunk()
611         {
612             var view = searchableViews[i];
613
614             if (++i >= searchableViews.length) {
615                 if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
616                     delete panel._currentSearchChunkIntervalIdentifier;
617                 clearInterval(chunkIntervalIdentifier);
618             }
619
620             if (!view)
621                 return;
622
623             view.currentQuery = query;
624             view.performSearch(query, boundFinishedCallback);
625         }
626
627         processChunk();
628
629         chunkIntervalIdentifier = setInterval(processChunk, 25);
630         this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
631     },
632
633     jumpToNextSearchResult: function()
634     {
635         if (!this.showView || !this._searchResults || !this._searchResults.length)
636             return;
637
638         var showFirstResult = false;
639
640         this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
641         if (this._currentSearchResultIndex === -1) {
642             this._currentSearchResultIndex = 0;
643             showFirstResult = true;
644         }
645
646         var currentView = this._searchResults[this._currentSearchResultIndex];
647
648         if (currentView.showingLastSearchResult()) {
649             if (++this._currentSearchResultIndex >= this._searchResults.length)
650                 this._currentSearchResultIndex = 0;
651             currentView = this._searchResults[this._currentSearchResultIndex];
652             showFirstResult = true;
653         }
654
655         if (currentView !== this.visibleView) {
656             this.showView(currentView);
657             WebInspector.searchController.focusSearchField();
658         }
659
660         if (showFirstResult)
661             currentView.jumpToFirstSearchResult();
662         else
663             currentView.jumpToNextSearchResult();
664     },
665
666     jumpToPreviousSearchResult: function()
667     {
668         if (!this.showView || !this._searchResults || !this._searchResults.length)
669             return;
670
671         var showLastResult = false;
672
673         this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
674         if (this._currentSearchResultIndex === -1) {
675             this._currentSearchResultIndex = 0;
676             showLastResult = true;
677         }
678
679         var currentView = this._searchResults[this._currentSearchResultIndex];
680
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;
686         }
687
688         if (currentView !== this.visibleView) {
689             this.showView(currentView);
690             WebInspector.searchController.focusSearchField();
691         }
692
693         if (showLastResult)
694             currentView.jumpToLastSearchResult();
695         else
696             currentView.jumpToPreviousSearchResult();
697     },
698
699     _searchableViews: function()
700     {
701         var views = [];
702
703         const visibleView = this.visibleView;
704         if (visibleView && visibleView.performSearch)
705             views.push(visibleView);
706
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)
712                 continue;
713             views.push(view);
714         }
715
716         return views;
717     },
718
719     searchMatchFound: function(view, matches)
720     {
721         view.profile._profilesTreeElement.searchMatches = matches;
722     },
723
724     searchCanceled: function()
725     {
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;
732             }
733         }
734
735         WebInspector.Panel.prototype.searchCanceled.call(this);
736
737         if (this._currentSearchChunkIntervalIdentifier) {
738             clearInterval(this._currentSearchChunkIntervalIdentifier);
739             delete this._currentSearchChunkIntervalIdentifier;
740         }
741
742         this._totalSearchMatches = 0;
743         this._currentSearchResultIndex = 0;
744         this._searchResults = [];
745
746         if (!this._profiles)
747             return;
748
749         for (var i = 0; i < this._profiles.length; ++i) {
750             var profile = this._profiles[i];
751             profile._profilesTreeElement.searchMatches = 0;
752         }
753     },
754
755     _updateInterface: function()
756     {
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();
766         } else {
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);
774         }
775     },
776
777     get profilerEnabled()
778     {
779         return this._profilerEnabled;
780     },
781
782     enableProfiler: function()
783     {
784         if (this._profilerEnabled)
785             return;
786         this._toggleProfiling(this.panelEnablerView.alwaysEnabled);
787     },
788
789     disableProfiler: function()
790     {
791         if (!this._profilerEnabled)
792             return;
793         this._toggleProfiling(this.panelEnablerView.alwaysEnabled);
794     },
795
796     _toggleProfiling: function(optionalAlways)
797     {
798         if (this._profilerEnabled) {
799             WebInspector.settings.profilerEnabled.set(false);
800             ProfilerAgent.disable();
801         } else {
802             WebInspector.settings.profilerEnabled.set(!!optionalAlways);
803             ProfilerAgent.enable();
804         }
805     },
806
807     _populateProfiles: function()
808     {
809         if (!this._profilerEnabled || this._profilesWereRequested)
810             return;
811
812         function populateCallback(error, profileHeaders) {
813             if (error)
814                 return;
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]);
820         }
821
822         ProfilerAgent.getProfileHeaders(populateCallback.bind(this));
823
824         this._profilesWereRequested = true;
825     },
826
827     sidebarResized: function(event)
828     {
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";
832     },
833
834     _setRecordingProfile: function(isProfiling)
835     {
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…"),
842                     uid: -1,
843                     isTemporary: true
844                 };
845             }
846             if (isProfiling) {
847                 this._addProfileHeader(this._temporaryRecordingProfile);
848                 WebInspector.userMetrics.ProfilesCPUProfileTaken.record();
849             } else
850                 this._removeProfileHeader(this._temporaryRecordingProfile);
851         }
852         this.updateProfileTypeButtons();
853     },
854
855     takeHeapSnapshot: function()
856     {
857         if (!this.hasTemporaryProfile(WebInspector.DetailedHeapshotProfileType.TypeId)) {
858             if (!this._temporaryTakingSnapshot) {
859                 this._temporaryTakingSnapshot = {
860                     typeId: WebInspector.DetailedHeapshotProfileType.TypeId,
861                     title: WebInspector.UIString("Snapshotting…"),
862                     uid: -1,
863                     isTemporary: true
864                 };
865             }
866             this._addProfileHeader(this._temporaryTakingSnapshot);
867         }
868         ProfilerAgent.takeHeapSnapshot();
869         WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
870     },
871
872     _reportHeapSnapshotProgress: function(done, total)
873     {
874         if (this.hasTemporaryProfile(WebInspector.DetailedHeapshotProfileType.TypeId)) {
875             this._temporaryTakingSnapshot.sidebarElement.subtitle = WebInspector.UIString("%.2f%%", (done / total) * 100);
876             this._temporaryTakingSnapshot.sidebarElement.wait = true;
877             if (done >= total)
878                 this._removeProfileHeader(this._temporaryTakingSnapshot);
879         }
880     },
881
882     _enableDetailedHeapProfiles: function(resetAgent)
883     {
884         if (resetAgent)
885             this._clearProfiles();
886         else
887             this._reset();
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;
893         this.detach();
894         this.show();
895     }
896 }
897
898 WebInspector.ProfilesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
899
900
901 WebInspector.ProfilerDispatcher = function(profiler)
902 {
903     this._profiler = profiler;
904 }
905
906 WebInspector.ProfilerDispatcher.prototype = {
907     profilerWasEnabled: function()
908     {
909         this._profiler._profilerWasEnabled();
910     },
911
912     profilerWasDisabled: function()
913     {
914         this._profiler._profilerWasDisabled();
915     },
916
917     resetProfiles: function()
918     {
919         this._profiler._reset();
920     },
921
922     addProfileHeader: function(profile)
923     {
924         this._profiler._addProfileHeader(profile);
925     },
926
927     addHeapSnapshotChunk: function(uid, chunk)
928     {
929         this._profiler._addHeapSnapshotChunk(uid, chunk);
930     },
931
932     finishHeapSnapshot: function(uid)
933     {
934         this._profiler._finishHeapSnapshot(uid);
935     },
936
937     setRecordingProfile: function(isProfiling)
938     {
939         this._profiler._setRecordingProfile(isProfiling);
940     },
941
942     reportHeapSnapshotProgress: function(done, total)
943     {
944         this._profiler._reportHeapSnapshotProgress(done, total);
945     }
946 }
947
948 WebInspector.ProfileSidebarTreeElement = function(profile, titleFormat, className)
949 {
950     this.profile = profile;
951     this._titleFormat = titleFormat;
952
953     if (this.profile.title.indexOf(UserInitiatedProfileName) === 0)
954         this._profileNumber = this.profile.title.substring(UserInitiatedProfileName.length + 1);
955
956     WebInspector.SidebarTreeElement.call(this, className, "", "", profile, false);
957
958     this.refreshTitles();
959 }
960
961 WebInspector.ProfileSidebarTreeElement.prototype = {
962     onselect: function()
963     {
964         if (!this._suppressOnSelect)
965             this.treeOutline.panel.showProfile(this.profile);
966     },
967
968     ondelete: function()
969     {
970         this.treeOutline.panel._removeProfileHeader(this.profile);
971         return true;
972     },
973
974     get mainTitle()
975     {
976         if (this._mainTitle)
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;
981     },
982
983     set mainTitle(x)
984     {
985         this._mainTitle = x;
986         this.refreshTitles();
987     },
988
989     set searchMatches(matches)
990     {
991         if (!matches) {
992             if (!this.bubbleElement)
993                 return;
994             this.bubbleElement.removeStyleClass("search-matches");
995             this.bubbleText = "";
996             return;
997         }
998
999         this.bubbleText = matches;
1000         this.bubbleElement.addStyleClass("search-matches");
1001     }
1002 }
1003
1004 WebInspector.ProfileSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
1005
1006 WebInspector.ProfileGroupSidebarTreeElement = function(title, subtitle)
1007 {
1008     WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
1009 }
1010
1011 WebInspector.ProfileGroupSidebarTreeElement.prototype = {
1012     onselect: function()
1013     {
1014         if (this.children.length > 0)
1015             WebInspector.panels.profiles.showProfile(this.children[this.children.length - 1].profile);
1016     }
1017 }
1018
1019 WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;