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