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