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