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