• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
32importScript("../sdk/CPUProfileModel.js");
33importScript("CountersGraph.js");
34importScript("Layers3DView.js");
35importScript("MemoryCountersGraph.js");
36importScript("TimelineModel.js");
37importScript("TimelineModelImpl.js");
38importScript("TimelineJSProfile.js");
39importScript("TimelineOverviewPane.js");
40importScript("TimelinePresentationModel.js");
41importScript("TracingTimelineModel.js");
42importScript("TimelineFrameModel.js");
43importScript("TimelineEventOverview.js");
44importScript("TimelineFrameOverview.js");
45importScript("TimelineMemoryOverview.js");
46importScript("TimelinePowerGraph.js");
47importScript("TimelinePowerOverview.js");
48importScript("TimelineFlameChart.js");
49importScript("TimelineUIUtils.js");
50importScript("TimelineUIUtilsImpl.js");
51importScript("TimelineView.js");
52importScript("TimelineTracingView.js");
53importScript("TimelineLayersView.js");
54importScript("TracingModel.js");
55importScript("TracingTimelineUIUtils.js");
56importScript("TransformController.js");
57
58/**
59 * @constructor
60 * @extends {WebInspector.Panel}
61 * @implements {WebInspector.TimelineModeViewDelegate}
62 * @implements {WebInspector.Searchable}
63 */
64WebInspector.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
158WebInspector.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.
164WebInspector.TimelinePanel.rowHeight = 18;
165WebInspector.TimelinePanel.headerHeight = 20;
166
167WebInspector.TimelinePanel.durationFilterPresetsMs = [0, 1, 15];
168
169WebInspector.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.createChild("div").textContent = 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 */
1118WebInspector.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
1131WebInspector.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 */
1182WebInspector.TimelineSelection = function()
1183{
1184}
1185
1186/**
1187 * @enum {string}
1188 */
1189WebInspector.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 */
1199WebInspector.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 */
1211WebInspector.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 */
1223WebInspector.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
1231WebInspector.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 */
1253WebInspector.TimelineModeView = function()
1254{
1255}
1256
1257WebInspector.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 */
1304WebInspector.TimelineModeViewDelegate = function() {}
1305
1306WebInspector.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 */
1329WebInspector.TimelineCategoryFilter = function()
1330{
1331    WebInspector.TimelineModel.Filter.call(this);
1332}
1333
1334WebInspector.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 */
1351WebInspector.TimelineIsLongFilter = function()
1352{
1353    WebInspector.TimelineModel.Filter.call(this);
1354    this._minimumRecordDuration = 0;
1355}
1356
1357WebInspector.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 */
1385WebInspector.TimelineTextFilter = function(uiUtils)
1386{
1387    WebInspector.TimelineModel.Filter.call(this);
1388    this._uiUtils = uiUtils;
1389}
1390
1391WebInspector.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 */
1425WebInspector.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
1442WebInspector.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}
1454