Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelinePanel.js
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Intel Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /**
33  * @constructor
34  * @extends {WebInspector.Panel}
35  * @implements {WebInspector.TimelineModeViewDelegate}
36  * @implements {WebInspector.Searchable}
37  */
38 WebInspector.TimelinePanel = function()
39 {
40     WebInspector.Panel.call(this, "timeline");
41     this.registerRequiredCSS("timelinePanel.css");
42     this.registerRequiredCSS("layersPanel.css");
43     this.registerRequiredCSS("filter.css");
44     this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false);
45
46     this._detailsLinkifier = new WebInspector.Linkifier();
47     this._windowStartTime = 0;
48     this._windowEndTime = Infinity;
49
50     // Create model.
51     if (Runtime.experiments.isEnabled("timelineOnTraceEvents")) {
52         this._tracingManager = new WebInspector.TracingManager();
53         this._tracingManager.addEventListener(WebInspector.TracingManager.Events.BufferUsage, this._onTracingBufferUsage, this);
54
55         this._tracingModel = new WebInspector.TracingModel();
56         this._uiUtils = new WebInspector.TracingTimelineUIUtils();
57         this._tracingTimelineModel = new WebInspector.TracingTimelineModel(this._tracingManager, this._tracingModel, this._uiUtils.hiddenRecordsFilter());
58         this._model = this._tracingTimelineModel;
59     } else {
60         this._uiUtils = new WebInspector.TimelineUIUtilsImpl();
61         this._model = new WebInspector.TimelineModelImpl();
62     }
63
64     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
65     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStopped, this._onRecordingStopped, this);
66     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
67     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingProgress, this._onRecordingProgress, this);
68     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordFilterChanged, this._refreshViews, this);
69     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
70
71     this._categoryFilter = new WebInspector.TimelineCategoryFilter(this._uiUtils);
72     this._durationFilter = new WebInspector.TimelineIsLongFilter();
73     this._textFilter = new WebInspector.TimelineTextFilter(this._uiUtils);
74
75     var hiddenEmptyRecordsFilter = this._uiUtils.hiddenEmptyRecordsFilter();
76     if (hiddenEmptyRecordsFilter)
77         this._model.addFilter(hiddenEmptyRecordsFilter);
78     this._model.addFilter(this._uiUtils.hiddenRecordsFilter());
79     this._model.addFilter(this._categoryFilter);
80     this._model.addFilter(this._durationFilter);
81     this._model.addFilter(this._textFilter);
82
83     /** @type {!Array.<!WebInspector.TimelineModeView>} */
84     this._currentViews = [];
85
86     this._overviewModeSetting = WebInspector.settings.createSetting("timelineOverviewMode", WebInspector.TimelinePanel.OverviewMode.Events);
87     this._flameChartEnabledSetting = WebInspector.settings.createSetting("timelineFlameChartEnabled", false);
88     this._createStatusBarItems();
89
90     var topPaneElement = this.element.createChild("div", "hbox");
91     topPaneElement.id = "timeline-overview-panel";
92
93     // Create top overview component.
94     this._overviewPane = new WebInspector.TimelineOverviewPane(this._model, this._uiUtils);
95     this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this));
96     this._overviewPane.show(topPaneElement);
97
98     this._createFileSelector();
99     this._registerShortcuts();
100
101     WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.WillReloadPage, this._willReloadPage, this);
102     WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.Load, this._loadEventFired, this);
103
104     // Create top level properties splitter.
105     this._detailsSplitView = new WebInspector.SplitView(false, true, "timelinePanelDetailsSplitViewState");
106     this._detailsSplitView.element.classList.add("timeline-details-split");
107     this._detailsSplitView.sidebarElement().classList.add("timeline-details");
108     this._detailsView = new WebInspector.TimelineDetailsView();
109     this._detailsSplitView.installResizer(this._detailsView.headerElement());
110     this._detailsView.show(this._detailsSplitView.sidebarElement());
111
112     this._searchableView = new WebInspector.SearchableView(this);
113     this._searchableView.setMinimumSize(0, 25);
114     this._searchableView.element.classList.add("searchable-view");
115     this._searchableView.show(this._detailsSplitView.mainElement());
116
117     this._stackView = new WebInspector.StackView(false);
118     this._stackView.show(this._searchableView.element);
119     this._stackView.element.classList.add("timeline-view-stack");
120
121     WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
122     WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
123     this._dockSideChanged();
124
125     this._onModeChanged();
126     this._detailsSplitView.show(this.element);
127     WebInspector.profilingLock().addEventListener(WebInspector.Lock.Events.StateChanged, this._onProfilingStateChanged, this);
128 }
129
130 WebInspector.TimelinePanel.OverviewMode = {
131     Events: "Events",
132     Frames: "Frames"
133 };
134
135 // Define row and header height, should be in sync with styles for timeline graphs.
136 WebInspector.TimelinePanel.rowHeight = 18;
137 WebInspector.TimelinePanel.headerHeight = 20;
138
139 WebInspector.TimelinePanel.durationFilterPresetsMs = [0, 1, 15];
140
141 WebInspector.TimelinePanel.prototype = {
142     /**
143      * @return {?WebInspector.SearchableView}
144      */
145     searchableView: function()
146     {
147         return this._searchableView;
148     },
149
150     wasShown: function()
151     {
152         if (!WebInspector.TimelinePanel._categoryStylesInitialized) {
153             WebInspector.TimelinePanel._categoryStylesInitialized = true;
154             var style = document.createElement("style");
155             var categories = WebInspector.TimelineUIUtils.categories();
156             style.textContent = Object.values(categories).map(WebInspector.TimelineUIUtils.createStyleRuleForCategory).join("\n");
157             document.head.appendChild(style);
158         }
159     },
160
161     _dockSideChanged: function()
162     {
163         var dockSide = WebInspector.dockController.dockSide();
164         var vertically = false;
165         if (dockSide === WebInspector.DockController.State.DockedToBottom)
166             vertically = true;
167         else
168             vertically = !WebInspector.settings.splitVerticallyWhenDockedToRight.get();
169         this._detailsSplitView.setVertical(vertically);
170         this._detailsView.setVertical(vertically);
171     },
172
173     /**
174      * @return {number}
175      */
176     windowStartTime: function()
177     {
178         if (this._windowStartTime)
179             return this._windowStartTime;
180         return this._model.minimumRecordTime();
181     },
182
183     /**
184      * @return {number}
185      */
186     windowEndTime: function()
187     {
188         if (this._windowEndTime < Infinity)
189             return this._windowEndTime;
190         return this._model.maximumRecordTime() || Infinity;
191     },
192
193     /**
194      * @param {!WebInspector.Event} event
195      */
196     _sidebarResized: function(event)
197     {
198         var width = /** @type {number} */ (event.data);
199         for (var i = 0; i < this._currentViews.length; ++i)
200             this._currentViews[i].setSidebarSize(width);
201     },
202
203     /**
204      * @param {!WebInspector.Event} event
205      */
206     _onWindowChanged: function(event)
207     {
208         this._windowStartTime = event.data.startTime;
209         this._windowEndTime = event.data.endTime;
210
211         for (var i = 0; i < this._currentViews.length; ++i)
212             this._currentViews[i].setWindowTimes(this._windowStartTime, this._windowEndTime);
213     },
214
215     /**
216      * @param {number} windowStartTime
217      * @param {number} windowEndTime
218      */
219     requestWindowTimes: function(windowStartTime, windowEndTime)
220     {
221         this._overviewPane.requestWindowTimes(windowStartTime, windowEndTime);
222     },
223
224     /**
225      * @return {!WebInspector.TimelineFrameModelBase}
226      */
227     _frameModel: function()
228     {
229         if (this._lazyFrameModel)
230             return this._lazyFrameModel;
231         if (this._tracingModel) {
232             var tracingFrameModel = new WebInspector.TracingTimelineFrameModel();
233             tracingFrameModel.addTraceEvents(this._tracingTimelineModel.inspectedTargetEvents(), this._tracingModel.sessionId() || "");
234             this._lazyFrameModel = tracingFrameModel;
235         } else {
236             var frameModel = new WebInspector.TimelineFrameModel();
237             frameModel.addRecords(this._model.records());
238             this._lazyFrameModel = frameModel;
239         }
240         return this._lazyFrameModel;
241     },
242
243     /**
244      * @return {!WebInspector.TimelineView}
245      */
246     _timelineView: function()
247     {
248         if (!this._lazyTimelineView)
249             this._lazyTimelineView = new WebInspector.TimelineView(this, this._model, this._uiUtils);
250         return this._lazyTimelineView;
251     },
252
253     /**
254      * @return {!WebInspector.View}
255      */
256     _layersView: function()
257     {
258         if (this._lazyLayersView)
259             return this._lazyLayersView;
260         this._lazyLayersView = new WebInspector.TimelineLayersView();
261         this._lazyLayersView.setTimelineModelAndDelegate(this._model, this);
262         return this._lazyLayersView;
263     },
264
265     _paintProfilerView: function()
266     {
267         if (this._lazyPaintProfilerView)
268             return this._lazyPaintProfilerView;
269         this._lazyPaintProfilerView = new WebInspector.TimelinePaintProfilerView();
270         return this._lazyPaintProfilerView;
271     },
272
273     /**
274      * @param {!WebInspector.TimelineModeView} modeView
275      */
276     _addModeView: function(modeView)
277     {
278         modeView.setWindowTimes(this.windowStartTime(), this.windowEndTime());
279         modeView.refreshRecords(this._textFilter._regex);
280         this._stackView.appendView(modeView.view(), "timelinePanelTimelineStackSplitViewState");
281         modeView.view().addEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._sidebarResized, this);
282         this._currentViews.push(modeView);
283     },
284
285     _removeAllModeViews: function()
286     {
287         for (var i = 0; i < this._currentViews.length; ++i) {
288             this._currentViews[i].removeEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._sidebarResized, this);
289             this._currentViews[i].dispose();
290         }
291         this._currentViews = [];
292         this._stackView.detachChildViews();
293     },
294
295     /**
296      * @param {string} name
297      * @param {!WebInspector.Setting} setting
298      * @param {string} tooltip
299      * @return {!Element}
300      */
301     _createSettingCheckbox: function(name, setting, tooltip)
302     {
303         if (!this._recordingOptionUIControls)
304             this._recordingOptionUIControls = [];
305
306         var checkboxElement = document.createElement("input");
307         var labelElement = WebInspector.SettingsUI.createSettingCheckbox(name, setting, true, checkboxElement, tooltip);
308         this._recordingOptionUIControls.push({ "label": labelElement, "checkbox": checkboxElement });
309         return labelElement;
310     },
311
312     _createStatusBarItems: function()
313     {
314         var panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
315         this._statusBarButtons = /** @type {!Array.<!WebInspector.StatusBarItem>} */ ([]);
316
317         this.toggleTimelineButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
318         this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked, this);
319         this._statusBarButtons.push(this.toggleTimelineButton);
320         panelStatusBarElement.appendChild(this.toggleTimelineButton.element);
321         this._updateToggleTimelineButton(false);
322
323         var clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
324         clearButton.addEventListener("click", this._onClearButtonClick, this);
325         this._statusBarButtons.push(clearButton);
326         panelStatusBarElement.appendChild(clearButton.element);
327
328         this._filterBar = this._createFilterBar();
329         panelStatusBarElement.appendChild(this._filterBar.filterButton().element);
330
331         var garbageCollectButton = new WebInspector.StatusBarButton(WebInspector.UIString("Collect Garbage"), "timeline-garbage-collect-status-bar-item");
332         garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this);
333         this._statusBarButtons.push(garbageCollectButton);
334         panelStatusBarElement.appendChild(garbageCollectButton.element);
335
336         var framesToggleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Frames mode"), "timeline-frames-status-bar-item");
337         framesToggleButton.toggled = this._overviewModeSetting.get() === WebInspector.TimelinePanel.OverviewMode.Frames;
338         framesToggleButton.addEventListener("click", this._overviewModeChanged.bind(this, framesToggleButton));
339         this._statusBarButtons.push(framesToggleButton);
340         panelStatusBarElement.appendChild(framesToggleButton.element);
341
342         if (Runtime.experiments.isEnabled("timelineOnTraceEvents")) {
343             var flameChartToggleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Tracing mode"), "timeline-flame-chart-status-bar-item");
344             flameChartToggleButton.toggled = this._flameChartEnabledSetting.get();
345             flameChartToggleButton.addEventListener("click", this._flameChartEnabledChanged.bind(this, flameChartToggleButton));
346             this._statusBarButtons.push(flameChartToggleButton);
347             panelStatusBarElement.appendChild(flameChartToggleButton.element);
348         }
349
350         this._captureStacksSetting = WebInspector.settings.createSetting("timelineCaptureStacks", true);
351         this._captureStacksSetting.addChangeListener(this._refreshViews, this);
352         panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Stacks"),
353                                                                       this._captureStacksSetting,
354                                                                       WebInspector.UIString("Capture JavaScript stack on every timeline event")));
355         this._captureMemorySetting = WebInspector.settings.createSetting("timelineCaptureMemory", false);
356         panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Memory"),
357                                                                       this._captureMemorySetting,
358                                                                       WebInspector.UIString("Capture memory information on every timeline event")));
359         this._captureMemorySetting.addChangeListener(this._onModeChanged, this);
360         if (Runtime.experiments.isEnabled("timelinePowerProfiler") &&
361             WebInspector.targetManager.mainTarget().hasCapability(WebInspector.Target.Capabilities.CanProfilePower)) {
362             this._capturePowerSetting = WebInspector.settings.createSetting("timelineCapturePower", false);
363             panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Power"),
364                                                                           this._capturePowerSetting,
365                                                                           WebInspector.UIString("Capture power information")));
366             this._capturePowerSetting.addChangeListener(this._onModeChanged, this);
367         }
368         if (Runtime.experiments.isEnabled("timelineOnTraceEvents")) {
369             this._captureLayersAndPicturesSetting = WebInspector.settings.createSetting("timelineCaptureLayersAndPictures", false);
370             panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Paint"),
371                                                                           this._captureLayersAndPicturesSetting,
372                                                                           WebInspector.UIString("Capture graphics layer positions and painted pictures")));
373         }
374
375         this._miscStatusBarItems = panelStatusBarElement.createChild("div", "status-bar-item");
376
377         this._filtersContainer = this.element.createChild("div", "timeline-filters-header hidden");
378         this._filtersContainer.appendChild(this._filterBar.filtersElement());
379         this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
380         this._filterBar.setName("timelinePanel");
381         if (!Runtime.experiments.isEnabled("timelineOnTraceEvents")) {
382             var targetsComboBox = new WebInspector.StatusBarComboBox(null);
383             panelStatusBarElement.appendChild(targetsComboBox.element);
384             new WebInspector.TargetsComboBoxController(targetsComboBox.selectElement(), targetsComboBox.element);
385         }
386     },
387
388     /**
389      * @return {!WebInspector.FilterBar}
390      */
391     _createFilterBar: function()
392     {
393         this._filterBar = new WebInspector.FilterBar();
394         this._filters = {};
395         this._filters._textFilterUI = new WebInspector.TextFilterUI();
396         this._filters._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
397         this._filterBar.addFilter(this._filters._textFilterUI);
398
399         var durationOptions = [];
400         for (var presetIndex = 0; presetIndex < WebInspector.TimelinePanel.durationFilterPresetsMs.length; ++presetIndex) {
401             var durationMs = WebInspector.TimelinePanel.durationFilterPresetsMs[presetIndex];
402             var durationOption = {};
403             if (!durationMs) {
404                 durationOption.label = WebInspector.UIString("All");
405                 durationOption.title = WebInspector.UIString("Show all records");
406             } else {
407                 durationOption.label = WebInspector.UIString("\u2265 %dms", durationMs);
408                 durationOption.title = WebInspector.UIString("Hide records shorter than %dms", durationMs);
409             }
410             durationOption.value = durationMs;
411             durationOptions.push(durationOption);
412         }
413         this._filters._durationFilterUI = new WebInspector.ComboBoxFilterUI(durationOptions);
414         this._filters._durationFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._durationFilterChanged, this);
415         this._filterBar.addFilter(this._filters._durationFilterUI);
416
417         this._filters._categoryFiltersUI = {};
418         var categoryTypes = [];
419         var categories = WebInspector.TimelineUIUtils.categories();
420         for (var categoryName in categories) {
421             var category = categories[categoryName];
422             if (category.overviewStripGroupIndex < 0)
423                 continue;
424             var filter = new WebInspector.CheckboxFilterUI(category.name, category.title);
425             this._filters._categoryFiltersUI[category.name] = filter;
426             filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._categoriesFilterChanged.bind(this, categoryName), this);
427             this._filterBar.addFilter(filter);
428         }
429         return this._filterBar;
430     },
431
432     _textFilterChanged: function(event)
433     {
434         var searchQuery = this._filters._textFilterUI.value();
435         this.searchCanceled();
436         this._textFilter.setRegex(searchQuery ? createPlainTextSearchRegex(searchQuery, "i") : null);
437     },
438
439     _durationFilterChanged: function()
440     {
441         var duration = this._filters._durationFilterUI.value();
442         var minimumRecordDuration = parseInt(duration, 10);
443         this._durationFilter.setMinimumRecordDuration(minimumRecordDuration);
444     },
445
446     _categoriesFilterChanged: function(name, event)
447     {
448         var categories = WebInspector.TimelineUIUtils.categories();
449         categories[name].hidden = !this._filters._categoryFiltersUI[name].checked();
450         this._categoryFilter.notifyFilterChanged();
451     },
452
453     _onFiltersToggled: function(event)
454     {
455         var toggled = /** @type {boolean} */ (event.data);
456         this._filtersContainer.classList.toggle("hidden", !toggled);
457         this.doResize();
458     },
459
460     /**
461      * @return {?WebInspector.ProgressIndicator}
462      */
463     _prepareToLoadTimeline: function()
464     {
465         if (this._operationInProgress)
466             return null;
467         if (this._recordingInProgress()) {
468             this._updateToggleTimelineButton(false);
469             this._stopRecording();
470         }
471         var progressIndicator = new WebInspector.ProgressIndicator();
472         progressIndicator.addEventListener(WebInspector.Progress.Events.Done, this._setOperationInProgress.bind(this, null));
473         this._setOperationInProgress(progressIndicator);
474         return progressIndicator;
475     },
476
477     /**
478      * @param {?WebInspector.ProgressIndicator} indicator
479      */
480     _setOperationInProgress: function(indicator)
481     {
482         this._operationInProgress = !!indicator;
483         for (var i = 0; i < this._statusBarButtons.length; ++i)
484             this._statusBarButtons[i].setEnabled(!this._operationInProgress);
485         this._miscStatusBarItems.removeChildren();
486         if (indicator)
487             this._miscStatusBarItems.appendChild(indicator.element);
488     },
489
490     _registerShortcuts: function()
491     {
492         this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.StartStopRecording, this._toggleTimelineButtonClicked.bind(this));
493         this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.SaveToFile, this._saveToFile.bind(this));
494         this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.LoadFromFile, this._selectFileToLoad.bind(this));
495     },
496
497     _createFileSelector: function()
498     {
499         if (this._fileSelectorElement)
500             this._fileSelectorElement.remove();
501
502         this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
503         this.element.appendChild(this._fileSelectorElement);
504     },
505
506     _contextMenu: function(event)
507     {
508         var contextMenu = new WebInspector.ContextMenu(event);
509         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save Timeline data\u2026" : "Save Timeline Data\u2026"), this._saveToFile.bind(this), this._operationInProgress);
510         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Load Timeline data\u2026" : "Load Timeline Data\u2026"), this._selectFileToLoad.bind(this), this._operationInProgress);
511         contextMenu.show();
512     },
513
514     /**
515      * @return {boolean}
516      */
517     _saveToFile: function()
518     {
519         if (this._operationInProgress)
520             return true;
521         this._model.saveToFile();
522         return true;
523     },
524
525     /**
526      * @return {boolean}
527      */
528     _selectFileToLoad: function() {
529         this._fileSelectorElement.click();
530         return true;
531     },
532
533     /**
534      * @param {!File} file
535      */
536     _loadFromFile: function(file)
537     {
538         var progressIndicator = this._prepareToLoadTimeline();
539         if (!progressIndicator)
540             return;
541         this._model.loadFromFile(file, progressIndicator);
542         this._createFileSelector();
543     },
544
545     _refreshViews: function()
546     {
547         for (var i = 0; i < this._currentViews.length; ++i) {
548             var view = this._currentViews[i];
549             view.refreshRecords(this._textFilter._regex);
550         }
551         this._updateSelectedRangeStats();
552     },
553
554     /**
555      * @param {!WebInspector.StatusBarButton} button
556      */
557     _overviewModeChanged: function(button)
558     {
559         var oldMode = this._overviewModeSetting.get();
560         if (oldMode === WebInspector.TimelinePanel.OverviewMode.Events) {
561             this._overviewModeSetting.set(WebInspector.TimelinePanel.OverviewMode.Frames);
562             button.toggled = true;
563         } else {
564             this._overviewModeSetting.set(WebInspector.TimelinePanel.OverviewMode.Events);
565             button.toggled = false;
566         }
567         this._onModeChanged();
568     },
569
570     /**
571      * @param {!WebInspector.StatusBarButton} button
572      */
573     _flameChartEnabledChanged: function(button)
574     {
575         var oldValue = this._flameChartEnabledSetting.get();
576         var newValue = !oldValue;
577         this._flameChartEnabledSetting.set(newValue);
578         button.toggled = newValue;
579         this._onModeChanged();
580     },
581
582     _onModeChanged: function()
583     {
584         this._stackView.detach();
585
586         var isFrameMode = this._overviewModeSetting.get() === WebInspector.TimelinePanel.OverviewMode.Frames;
587         this._removeAllModeViews();
588         this._overviewControls = [];
589
590         if (isFrameMode)
591             this._overviewControls.push(new WebInspector.TimelineFrameOverview(this._model, this._frameModel()));
592         else
593             this._overviewControls.push(new WebInspector.TimelineEventOverview(this._model, this._uiUtils));
594
595         if (this._tracingTimelineModel && this._flameChartEnabledSetting.get())
596             this._addModeView(new WebInspector.TimelineFlameChart(this, this._tracingTimelineModel, this._frameModel()));
597         else
598             this._addModeView(this._timelineView());
599
600         if (this._captureMemorySetting.get()) {
601             if (!isFrameMode)  // Frame mode skews time, don't render aux overviews.
602                 this._overviewControls.push(new WebInspector.TimelineMemoryOverview(this._model, this._uiUtils));
603             this._addModeView(new WebInspector.MemoryCountersGraph(this, this._model, this._uiUtils));
604         }
605
606         if (this._capturePowerSetting && this._capturePowerSetting.get() &&
607             WebInspector.targetManager.mainTarget().hasCapability(WebInspector.Target.Capabilities.CanProfilePower)) {
608             if (!isFrameMode)  // Frame mode skews time, don't render aux overviews.
609                 this._overviewControls.push(new WebInspector.TimelinePowerOverview(this._model));
610             this._addModeView(new WebInspector.TimelinePowerGraph(this, this._model));
611         }
612
613         if (this._lazyTimelineView)
614             this._lazyTimelineView.setFrameModel(isFrameMode ? this._frameModel() : null);
615
616         this._overviewPane.setOverviewControls(this._overviewControls);
617         this.doResize();
618         this._updateSelectedRangeStats();
619
620         this._stackView.show(this._searchableView.element);
621     },
622
623     /**
624      * @param {boolean} enabled
625      */
626     _setUIControlsEnabled: function(enabled) {
627         function handler(uiControl)
628         {
629             uiControl.checkbox.disabled = !enabled;
630             uiControl.label.classList.toggle("dimmed", !enabled);
631         }
632         this._recordingOptionUIControls.forEach(handler);
633         WebInspector.inspectorView.setCurrentPanelLocked(!enabled);
634     },
635
636     /**
637      * @param {boolean} userInitiated
638      */
639     _startRecording: function(userInitiated)
640     {
641         this._userInitiatedRecording = userInitiated;
642         this._model.startRecording(this._captureStacksSetting.get(), this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting && this._captureLayersAndPicturesSetting.get());
643         if (this._lazyFrameModel)
644             this._lazyFrameModel.setMergeRecords(false);
645
646         for (var i = 0; i < this._overviewControls.length; ++i)
647             this._overviewControls[i].timelineStarted();
648
649         if (userInitiated)
650             WebInspector.userMetrics.TimelineStarted.record();
651         this._setUIControlsEnabled(false);
652     },
653
654     _stopRecording: function()
655     {
656         this._stopPending = true;
657         this._updateToggleTimelineButton(false);
658         this._userInitiatedRecording = false;
659         this._model.stopRecording();
660         if (this._progressElement)
661             this._updateProgress(WebInspector.UIString("Retrieving events\u2026"));
662
663         for (var i = 0; i < this._overviewControls.length; ++i)
664             this._overviewControls[i].timelineStopped();
665         this._setUIControlsEnabled(true);
666     },
667
668     _onProfilingStateChanged: function()
669     {
670         this._updateToggleTimelineButton(this.toggleTimelineButton.toggled);
671     },
672
673     /**
674      * @param {boolean} toggled
675      */
676     _updateToggleTimelineButton: function(toggled)
677     {
678         var isAcquiredInSomeTarget = WebInspector.profilingLock().isAcquired();
679         this.toggleTimelineButton.toggled = toggled;
680         if (toggled) {
681             this.toggleTimelineButton.title = WebInspector.UIString("Stop");
682             this.toggleTimelineButton.setEnabled(true);
683         } else if (this._stopPending) {
684             this.toggleTimelineButton.title = WebInspector.UIString("Stop pending");
685             this.toggleTimelineButton.setEnabled(false);
686         } else if (isAcquiredInSomeTarget) {
687             this.toggleTimelineButton.title = WebInspector.anotherProfilerActiveLabel();
688             this.toggleTimelineButton.setEnabled(false);
689         } else {
690             this.toggleTimelineButton.title = WebInspector.UIString("Record");
691             this.toggleTimelineButton.setEnabled(true);
692         }
693     },
694
695     /**
696      * @return {boolean}
697      */
698     _toggleTimelineButtonClicked: function()
699     {
700         if (!this.toggleTimelineButton.enabled())
701             return true;
702         if (this._operationInProgress)
703             return true;
704         if (this._recordingInProgress())
705             this._stopRecording();
706         else
707             this._startRecording(true);
708         return true;
709     },
710
711     _garbageCollectButtonClicked: function()
712     {
713         var targets = WebInspector.targetManager.targets();
714         for (var i = 0; i < targets.length; ++i)
715             targets[i].heapProfilerAgent().collectGarbage();
716     },
717
718     _onClearButtonClick: function()
719     {
720         if (this._tracingModel)
721             this._tracingModel.reset();
722         this._model.reset();
723     },
724
725     _onRecordsCleared: function()
726     {
727         this.requestWindowTimes(0, Infinity);
728         delete this._selection;
729         if (this._lazyFrameModel)
730             this._lazyFrameModel.reset();
731         for (var i = 0; i < this._currentViews.length; ++i)
732             this._currentViews[i].reset();
733         for (var i = 0; i < this._overviewControls.length; ++i)
734             this._overviewControls[i].reset();
735         this._updateSelectedRangeStats();
736     },
737
738     _onRecordingStarted: function()
739     {
740         this._updateToggleTimelineButton(true);
741         this._updateProgress(WebInspector.UIString("%d events collected", 0));
742     },
743
744     _recordingInProgress: function()
745     {
746         return this.toggleTimelineButton.toggled;
747     },
748
749     /**
750      * @param {!WebInspector.Event} event
751      */
752     _onRecordingProgress: function(event)
753     {
754         this._updateProgress(WebInspector.UIString("%d events collected", event.data));
755     },
756
757     /**
758      * @param {!WebInspector.Event} event
759      */
760     _onTracingBufferUsage: function(event)
761     {
762         var usage = /** @type {number} */ (event.data);
763         this._updateProgress(WebInspector.UIString("Buffer usage %d%", Math.round(usage * 100)));
764     },
765
766     /**
767      * @param {string} progressMessage
768      */
769     _updateProgress: function(progressMessage)
770     {
771         if (!this._progressElement)
772             this._showProgressPane();
773         this._progressElement.textContent = progressMessage;
774     },
775
776     _showProgressPane: function()
777     {
778         this._hideProgressPane();
779         this._progressElement = this._detailsSplitView.mainElement().createChild("div", "timeline-progress-pane");
780     },
781
782     _hideProgressPane: function()
783     {
784         if (this._progressElement)
785             this._progressElement.remove();
786         delete this._progressElement;
787     },
788
789     _onRecordingStopped: function()
790     {
791         this._stopPending = false;
792         this._updateToggleTimelineButton(false);
793         if (this._lazyFrameModel) {
794             this._lazyFrameModel.reset();
795             if (this._tracingTimelineModel)
796                 this._lazyFrameModel.addTraceEvents(this._tracingTimelineModel.inspectedTargetEvents(), this._tracingModel.sessionId());
797             else
798                 this._lazyFrameModel.addRecords(this._model.records());
799         }
800         if (this._tracingTimelineModel) {
801             this.requestWindowTimes(this._tracingTimelineModel.minimumRecordTime(), this._tracingTimelineModel.maximumRecordTime());
802             this._refreshViews();
803         }
804         this._hideProgressPane();
805         this._overviewPane.update();
806     },
807
808     _onRecordAdded: function(event)
809     {
810         this._addRecord(/** @type {!WebInspector.TimelineModel.Record} */(event.data));
811     },
812
813     /**
814      * @param {!WebInspector.TimelineModel.Record} record
815      */
816     _addRecord: function(record)
817     {
818         if (this._lazyFrameModel && !this._tracingModel)
819             this._lazyFrameModel.addRecord(record);
820         for (var i = 0; i < this._currentViews.length; ++i)
821             this._currentViews[i].addRecord(record);
822         this._updateSearchHighlight(false, true);
823     },
824
825     /**
826      * @param {!WebInspector.Event} event
827      */
828     _willReloadPage: function(event)
829     {
830         if (this._operationInProgress || this._userInitiatedRecording || !this.isShowing())
831             return;
832         this._startRecording(false);
833     },
834
835     /**
836      * @param {!WebInspector.Event} event
837      */
838     _loadEventFired: function(event)
839     {
840         if (!this._recordingInProgress() || this._userInitiatedRecording)
841             return;
842         this._stopRecording();
843     },
844
845     // WebInspector.Searchable implementation
846
847     jumpToNextSearchResult: function()
848     {
849         if (!this._searchResults || !this._searchResults.length)
850             return;
851         var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : -1;
852         this._jumpToSearchResult(index + 1);
853     },
854
855     jumpToPreviousSearchResult: function()
856     {
857         if (!this._searchResults || !this._searchResults.length)
858             return;
859         var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : 0;
860         this._jumpToSearchResult(index - 1);
861     },
862
863     _jumpToSearchResult: function(index)
864     {
865         this._selectSearchResult((index + this._searchResults.length) % this._searchResults.length);
866         this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, true);
867     },
868
869     _selectSearchResult: function(index)
870     {
871         this._selectedSearchResult = this._searchResults[index];
872         this._searchableView.updateCurrentMatchIndex(index);
873     },
874
875     _clearHighlight: function()
876     {
877         this._currentViews[0].highlightSearchResult(null);
878     },
879
880     /**
881      * @param {boolean} revealRecord
882      * @param {boolean} shouldJump
883      * @param {boolean=} jumpBackwards
884      */
885     _updateSearchHighlight: function(revealRecord, shouldJump, jumpBackwards)
886     {
887         if (!this._textFilter.isEmpty() || !this._searchRegex) {
888             this._clearHighlight();
889             return;
890         }
891
892         if (!this._searchResults)
893             this._updateSearchResults(shouldJump, jumpBackwards);
894         this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, revealRecord);
895     },
896
897     /**
898      * @param {boolean} shouldJump
899      * @param {boolean=} jumpBackwards
900      */
901     _updateSearchResults: function(shouldJump, jumpBackwards)
902     {
903         var searchRegExp = this._searchRegex;
904         if (!searchRegExp)
905             return;
906
907         var matches = [];
908
909         /**
910          * @param {!WebInspector.TimelineModel.Record} record
911          * @this {WebInspector.TimelinePanel}
912          */
913         function processRecord(record)
914         {
915             if (record.endTime() < this._windowStartTime ||
916                 record.startTime() > this._windowEndTime)
917                 return;
918             if (this._uiUtils.testContentMatching(record, searchRegExp))
919                 matches.push(record);
920         }
921         this._model.forAllFilteredRecords(processRecord.bind(this));
922
923         var matchesCount = matches.length;
924         if (matchesCount) {
925             this._searchResults = matches;
926             this._searchableView.updateSearchMatchesCount(matchesCount);
927
928             var selectedIndex = matches.indexOf(this._selectedSearchResult);
929             if (shouldJump && selectedIndex === -1)
930                 selectedIndex = jumpBackwards ? this._searchResults.length - 1 : 0;
931             this._selectSearchResult(selectedIndex);
932         } else {
933             this._searchableView.updateSearchMatchesCount(0);
934             delete this._selectedSearchResult;
935         }
936     },
937
938     searchCanceled: function()
939     {
940         this._clearHighlight();
941         delete this._searchResults;
942         delete this._selectedSearchResult;
943         delete this._searchRegex;
944     },
945
946     /**
947      * @param {string} query
948      * @param {boolean} shouldJump
949      * @param {boolean=} jumpBackwards
950      */
951     performSearch: function(query, shouldJump, jumpBackwards)
952     {
953         this._searchRegex = createPlainTextSearchRegex(query, "i");
954         delete this._searchResults;
955         this._updateSearchHighlight(true, shouldJump, jumpBackwards);
956     },
957
958     _updateSelectionDetails: function()
959     {
960         if (!this._selection) {
961             this._updateSelectedRangeStats();
962             return;
963         }
964         switch (this._selection.type()) {
965         case WebInspector.TimelineSelection.Type.Record:
966             var record = /** @type {!WebInspector.TimelineModel.Record} */ (this._selection.object());
967             if (this._tracingTimelineModel) {
968                 var event = record.traceEvent();
969                 this._uiUtils.generateDetailsContent(record, this._model, this._detailsLinkifier, this._appendDetailsTabsForTraceEventAndShowDetails.bind(this, event));
970                 break;
971             }
972             var title = this._uiUtils.titleForRecord(record);
973             this._uiUtils.generateDetailsContent(record, this._model, this._detailsLinkifier, this.showInDetails.bind(this, title));
974             break;
975         case WebInspector.TimelineSelection.Type.TraceEvent:
976             var event = /** @type {!WebInspector.TracingModel.Event} */ (this._selection.object());
977             WebInspector.TracingTimelineUIUtils.buildTraceEventDetails(event, this._tracingTimelineModel, this._detailsLinkifier, this._appendDetailsTabsForTraceEventAndShowDetails.bind(this, event));
978             break;
979         case WebInspector.TimelineSelection.Type.Frame:
980             var frame = /** @type {!WebInspector.TimelineFrame} */ (this._selection.object());
981             this.showInDetails(WebInspector.UIString("Frame"), WebInspector.TimelineUIUtils.generateDetailsContentForFrame(this._lazyFrameModel, frame));
982             if (frame.layerTree) {
983                 var layersView = this._layersView();
984                 layersView.showLayerTree(frame.layerTree, frame.paints);
985                 this._detailsView.appendTab("layers", WebInspector.UIString("Layers"), layersView);
986             }
987             break;
988         }
989     },
990
991     /**
992      * @param {!WebInspector.TracingModel.Event} event
993      * @param {!Node} content
994      */
995     _appendDetailsTabsForTraceEventAndShowDetails: function(event, content)
996     {
997         var title = WebInspector.TracingTimelineUIUtils.eventStyle(event).title;
998         this.showInDetails(title, content);
999         if (!event.picture)
1000             return;
1001         var paintProfilerView = this._paintProfilerView();
1002         this._detailsView.appendTab("paintProfiler", WebInspector.UIString("Paint Profiler"), paintProfilerView);
1003         event.picture.requestObject(onGotObject);
1004         function onGotObject(result)
1005         {
1006             if (!result || !result["skp64"])
1007                 return;
1008             paintProfilerView.setPicture(event.thread.target(), result["skp64"]);
1009         }
1010     },
1011
1012     _updateSelectedRangeStats: function()
1013     {
1014         if (this._selection)
1015             return;
1016
1017         var startTime = this._windowStartTime;
1018         var endTime = this._windowEndTime;
1019         var uiUtils = this._uiUtils;
1020
1021         // Return early in case 0 selection window.
1022         if (startTime < 0)
1023             return;
1024
1025         var aggregatedStats = {};
1026
1027         /**
1028          * @param {number} value
1029          * @param {!WebInspector.TimelineModel.Record} task
1030          * @return {number}
1031          */
1032         function compareEndTime(value, task)
1033         {
1034             return value < task.endTime() ? -1 : 1;
1035         }
1036
1037         /**
1038          * @param {!WebInspector.TimelineModel.Record} record
1039          */
1040         function aggregateTimeForRecordWithinWindow(record)
1041         {
1042             if (!record.endTime() || record.endTime() < startTime || record.startTime() > endTime)
1043                 return;
1044
1045             var childrenTime = 0;
1046             var children = record.children() || [];
1047             for (var i = 0; i < children.length; ++i) {
1048                 var child = children[i];
1049                 if (!child.endTime() || child.endTime() < startTime || child.startTime() > endTime)
1050                     continue;
1051                 childrenTime += Math.min(endTime, child.endTime()) - Math.max(startTime, child.startTime());
1052                 aggregateTimeForRecordWithinWindow(child);
1053             }
1054             var categoryName = uiUtils.categoryForRecord(record).name;
1055             var ownTime = Math.min(endTime, record.endTime()) - Math.max(startTime, record.startTime()) - childrenTime;
1056             aggregatedStats[categoryName] = (aggregatedStats[categoryName] || 0) + ownTime;
1057         }
1058
1059         var mainThreadTasks = this._model.mainThreadTasks();
1060         var taskIndex = insertionIndexForObjectInListSortedByFunction(startTime, mainThreadTasks, compareEndTime);
1061         for (; taskIndex < mainThreadTasks.length; ++taskIndex) {
1062             var task = mainThreadTasks[taskIndex];
1063             if (task.startTime() > endTime)
1064                 break;
1065             aggregateTimeForRecordWithinWindow(task);
1066         }
1067
1068         var aggregatedTotal = 0;
1069         for (var categoryName in aggregatedStats)
1070             aggregatedTotal += aggregatedStats[categoryName];
1071         aggregatedStats["idle"] = Math.max(0, endTime - startTime - aggregatedTotal);
1072
1073         var pieChartContainer = document.createElement("div");
1074         pieChartContainer.classList.add("vbox", "timeline-range-summary");
1075         var startOffset = startTime - this._model.minimumRecordTime();
1076         var endOffset = endTime - this._model.minimumRecordTime();
1077         var title = WebInspector.UIString("Range: %s \u2013 %s", Number.millisToString(startOffset), Number.millisToString(endOffset));
1078
1079         for (var i = 0; i < this._overviewControls.length; ++i) {
1080             if (this._overviewControls[i] instanceof WebInspector.TimelinePowerOverview) {
1081                 var energy = this._overviewControls[i].calculateEnergy(startTime, endTime);
1082                 title += WebInspector.UIString("  Energy: %.2f Joules", energy);
1083                 title += WebInspector.UIString("  Accuracy: %s", WebInspector.powerProfiler.getAccuracyLevel());
1084                 break;
1085             }
1086         }
1087         pieChartContainer.createChild("div").textContent = title;
1088         pieChartContainer.appendChild(WebInspector.TimelineUIUtils.generatePieChart(aggregatedStats));
1089         this.showInDetails(WebInspector.UIString("Selected Range"), pieChartContainer);
1090     },
1091
1092     /**
1093      * @param {?WebInspector.TimelineSelection} selection
1094      */
1095     select: function(selection)
1096     {
1097         this._detailsLinkifier.reset();
1098         this._selection = selection;
1099
1100         for (var i = 0; i < this._currentViews.length; ++i) {
1101             var view = this._currentViews[i];
1102             view.setSelection(selection);
1103         }
1104         this._updateSelectionDetails();
1105     },
1106
1107     /**
1108      * @param {string} title
1109      * @param {!Node} node
1110      */
1111     showInDetails: function(title, node)
1112     {
1113         this._detailsView.setContent(title, node);
1114     },
1115
1116     __proto__: WebInspector.Panel.prototype
1117 }
1118
1119 /**
1120  * @constructor
1121  * @extends {WebInspector.TabbedPane}
1122  */
1123 WebInspector.TimelineDetailsView = function()
1124 {
1125     WebInspector.TabbedPane.call(this);
1126
1127     this._recordTitleElement = document.createElement("div");
1128     this._recordTitleElement.classList.add("record-title");
1129     this.headerElement().insertBefore(this._recordTitleElement, this.headerElement().firstChild)
1130     this._defaultDetailsView = new WebInspector.VBox();
1131     this._defaultDetailsView.element.classList.add("timeline-details-view");
1132     this._defaultDetailsContentElement = this._defaultDetailsView.element.createChild("div", "timeline-details-view-body");
1133
1134     this.appendTab("default", WebInspector.UIString("Details"), this._defaultDetailsView);
1135
1136     this.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
1137 }
1138
1139 WebInspector.TimelineDetailsView.prototype = {
1140     /**
1141      * @param {string} title
1142      * @param {!Node} node
1143      */
1144     setContent: function(title, node)
1145     {
1146         this._recordTitleElement.textContent = title;
1147         var otherTabs = this.otherTabs("default");
1148         for (var i = 0; i < otherTabs.length; ++i)
1149             this.closeTab(otherTabs[i]);
1150         this._defaultDetailsContentElement.removeChildren();
1151         this._defaultDetailsContentElement.appendChild(node);
1152     },
1153
1154     /**
1155      * @param {boolean} vertical
1156      */
1157     setVertical: function(vertical)
1158     {
1159         this._defaultDetailsContentElement.classList.toggle("hbox", !vertical);
1160         this._defaultDetailsContentElement.classList.toggle("vbox", vertical);
1161     },
1162
1163     /**
1164      * @override
1165      * @param {string} id
1166      * @param {string} tabTitle
1167      * @param {!WebInspector.View} view
1168      * @param {string=} tabTooltip
1169      * @param {boolean=} userGesture
1170      * @param {boolean=} isCloseable
1171      */
1172     appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable)
1173     {
1174         WebInspector.TabbedPane.prototype.appendTab.call(this, id, tabTitle, view, tabTooltip);
1175         if (this._lastUserSelectedTabId !== this.selectedTabId)
1176             this.selectTab(id);
1177     },
1178
1179     _tabSelected: function(event)
1180     {
1181         if (!event.data.isUserGesture)
1182             return;
1183
1184         this._lastUserSelectedTabId = event.data.tabId;
1185     },
1186
1187     __proto__: WebInspector.TabbedPane.prototype
1188 }
1189
1190 /**
1191  * @constructor
1192  */
1193 WebInspector.TimelineSelection = function()
1194 {
1195 }
1196
1197 /**
1198  * @enum {string}
1199  */
1200 WebInspector.TimelineSelection.Type = {
1201     Record: "Record",
1202     Frame: "Frame",
1203     TraceEvent: "TraceEvent",
1204 };
1205
1206 /**
1207  * @param {!WebInspector.TimelineModel.Record} record
1208  * @return {!WebInspector.TimelineSelection}
1209  */
1210 WebInspector.TimelineSelection.fromRecord = function(record)
1211 {
1212     var selection = new WebInspector.TimelineSelection();
1213     selection._type = WebInspector.TimelineSelection.Type.Record;
1214     selection._object = record;
1215     return selection;
1216 }
1217
1218 /**
1219  * @param {!WebInspector.TimelineFrame} frame
1220  * @return {!WebInspector.TimelineSelection}
1221  */
1222 WebInspector.TimelineSelection.fromFrame = function(frame)
1223 {
1224     var selection = new WebInspector.TimelineSelection();
1225     selection._type = WebInspector.TimelineSelection.Type.Frame;
1226     selection._object = frame;
1227     return selection;
1228 }
1229
1230 /**
1231  * @param {!WebInspector.TracingModel.Event} event
1232  * @return {!WebInspector.TimelineSelection}
1233  */
1234 WebInspector.TimelineSelection.fromTraceEvent = function(event)
1235 {
1236     var selection = new WebInspector.TimelineSelection();
1237     selection._type = WebInspector.TimelineSelection.Type.TraceEvent;
1238     selection._object = event;
1239     return selection;
1240 }
1241
1242 WebInspector.TimelineSelection.prototype = {
1243     /**
1244      * @return {!WebInspector.TimelineSelection.Type}
1245      */
1246     type: function()
1247     {
1248         return this._type;
1249     },
1250
1251     /**
1252      * @return {!Object}
1253      */
1254     object: function()
1255     {
1256         return this._object;
1257     }
1258 };
1259
1260 /**
1261  * @interface
1262  * @extends {WebInspector.EventTarget}
1263  */
1264 WebInspector.TimelineModeView = function()
1265 {
1266 }
1267
1268 WebInspector.TimelineModeView.prototype = {
1269     /**
1270      * @return {!WebInspector.View}
1271      */
1272     view: function() {},
1273
1274     dispose: function() {},
1275
1276     reset: function() {},
1277
1278     /**
1279      * @param {?RegExp} textFilter
1280      */
1281     refreshRecords: function(textFilter) {},
1282
1283     /**
1284      * @param {!WebInspector.TimelineModel.Record} record
1285      */
1286     addRecord: function(record) {},
1287
1288     /**
1289      * @param {?WebInspector.TimelineModel.Record} record
1290      * @param {string=} regex
1291      * @param {boolean=} selectRecord
1292      */
1293     highlightSearchResult: function(record, regex, selectRecord) {},
1294
1295     /**
1296      * @param {number} startTime
1297      * @param {number} endTime
1298      */
1299     setWindowTimes: function(startTime, endTime) {},
1300
1301     /**
1302      * @param {number} width
1303      */
1304     setSidebarSize: function(width) {},
1305
1306     /**
1307      * @param {?WebInspector.TimelineSelection} selection
1308      */
1309     setSelection: function(selection) {},
1310 }
1311
1312 /**
1313  * @interface
1314  */
1315 WebInspector.TimelineModeViewDelegate = function() {}
1316
1317 WebInspector.TimelineModeViewDelegate.prototype = {
1318     /**
1319      * @param {number} startTime
1320      * @param {number} endTime
1321      */
1322     requestWindowTimes: function(startTime, endTime) {},
1323
1324     /**
1325      * @param {?WebInspector.TimelineSelection} selection
1326      */
1327     select: function(selection) {},
1328
1329     /**
1330      * @param {string} title
1331      * @param {!Node} node
1332      */
1333     showInDetails: function(title, node) {},
1334 }
1335
1336 /**
1337  * @constructor
1338  * @extends {WebInspector.TimelineModel.Filter}
1339  * @param {!WebInspector.TimelineUIUtils} uiUtils
1340  */
1341 WebInspector.TimelineCategoryFilter = function(uiUtils)
1342 {
1343     WebInspector.TimelineModel.Filter.call(this);
1344     this._uiUtils = uiUtils;
1345 }
1346
1347 WebInspector.TimelineCategoryFilter.prototype = {
1348     /**
1349      * @param {!WebInspector.TimelineModel.Record} record
1350      * @return {boolean}
1351      */
1352     accept: function(record)
1353     {
1354         return !this._uiUtils.categoryForRecord(record).hidden;
1355     },
1356
1357     __proto__: WebInspector.TimelineModel.Filter.prototype
1358 }
1359
1360 /**
1361  * @constructor
1362  * @extends {WebInspector.TimelineModel.Filter}
1363  */
1364 WebInspector.TimelineIsLongFilter = function()
1365 {
1366     WebInspector.TimelineModel.Filter.call(this);
1367     this._minimumRecordDuration = 0;
1368 }
1369
1370 WebInspector.TimelineIsLongFilter.prototype = {
1371     /**
1372      * @param {number} value
1373      */
1374     setMinimumRecordDuration: function(value)
1375     {
1376         this._minimumRecordDuration = value;
1377         this.notifyFilterChanged();
1378     },
1379
1380     /**
1381      * @param {!WebInspector.TimelineModel.Record} record
1382      * @return {boolean}
1383      */
1384     accept: function(record)
1385     {
1386         return this._minimumRecordDuration ? ((record.endTime() - record.startTime()) >= this._minimumRecordDuration) : true;
1387     },
1388
1389     __proto__: WebInspector.TimelineModel.Filter.prototype
1390
1391 }
1392
1393 /**
1394  * @constructor
1395  * @extends {WebInspector.TimelineModel.Filter}
1396  * @param {!WebInspector.TimelineUIUtils} uiUtils
1397  */
1398 WebInspector.TimelineTextFilter = function(uiUtils)
1399 {
1400     WebInspector.TimelineModel.Filter.call(this);
1401     this._uiUtils = uiUtils;
1402 }
1403
1404 WebInspector.TimelineTextFilter.prototype = {
1405     /**
1406      * @return {boolean}
1407      */
1408     isEmpty: function()
1409     {
1410         return !this._regex;
1411     },
1412
1413     /**
1414      * @param {?RegExp} regex
1415      */
1416     setRegex: function(regex)
1417     {
1418         this._regex = regex;
1419         this.notifyFilterChanged();
1420     },
1421
1422     /**
1423      * @param {!WebInspector.TimelineModel.Record} record
1424      * @return {boolean}
1425      */
1426     accept: function(record)
1427     {
1428         return !this._regex || this._uiUtils.testContentMatching(record, this._regex);
1429     },
1430
1431     __proto__: WebInspector.TimelineModel.Filter.prototype
1432 }