Home | History | Annotate | Download | only in timeline
      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.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  */
   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 }
   1454