| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * Copyright (C) 2012 Intel Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Panel} |
| * @implements {WebInspector.TimelineLifecycleDelegate} |
| * @implements {WebInspector.TimelineModeViewDelegate} |
| * @implements {WebInspector.Searchable} |
| */ |
| WebInspector.TimelinePanel = function() |
| { |
| WebInspector.Panel.call(this, "timeline"); |
| this.registerRequiredCSS("timeline/timelinePanel.css"); |
| this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false); |
| this._dropTarget = new WebInspector.DropTarget(this.element, [WebInspector.DropTarget.Types.Files, WebInspector.DropTarget.Types.URIList], WebInspector.UIString("Drop timeline file or URL here"), this._handleDrop.bind(this)); |
| |
| this._state = WebInspector.TimelinePanel.State.Idle; |
| this._detailsLinkifier = new WebInspector.Linkifier(); |
| this._windowStartTime = 0; |
| this._windowEndTime = Infinity; |
| this._millisecondsToRecordAfterLoadEvent = 3000; |
| this._toggleRecordAction = /** @type {!WebInspector.Action }*/ (WebInspector.actionRegistry.action("timeline.toggle-recording")); |
| |
| /** @type {!Array<!WebInspector.TimelineModel.Filter>} */ |
| this._filters = []; |
| if (!Runtime.experiments.isEnabled("timelineShowAllEvents")) { |
| this._filters.push(WebInspector.TimelineUIUtils.visibleEventsFilter()); |
| this._filters.push(new WebInspector.ExcludeTopLevelFilter()); |
| } |
| |
| // Create models. |
| this._tracingModelBackingStorage = new WebInspector.TempFileBackingStorage("tracing"); |
| this._tracingModel = new WebInspector.TracingModel(this._tracingModelBackingStorage); |
| this._model = new WebInspector.TimelineModel(WebInspector.TimelineUIUtils.visibleEventsFilter()); |
| this._frameModel = new WebInspector.TimelineFrameModel(event => WebInspector.TimelineUIUtils.eventStyle(event).category.name); |
| this._irModel = new WebInspector.TimelineIRModel(); |
| |
| if (Runtime.experiments.isEnabled("cpuThrottling")) |
| this._cpuThrottlingManager = new WebInspector.CPUThrottlingManager(); |
| |
| /** @type {!Array.<!WebInspector.TimelineModeView>} */ |
| this._currentViews = []; |
| |
| this._captureNetworkSetting = WebInspector.settings.createSetting("timelineCaptureNetwork", false); |
| this._captureJSProfileSetting = WebInspector.settings.createSetting("timelineEnableJSSampling", true); |
| this._captureMemorySetting = WebInspector.settings.createSetting("timelineCaptureMemory", false); |
| this._captureLayersAndPicturesSetting = WebInspector.settings.createSetting("timelineCaptureLayersAndPictures", false); |
| this._captureFilmStripSetting = WebInspector.settings.createSetting("timelineCaptureFilmStrip", false); |
| |
| this._panelToolbar = new WebInspector.Toolbar("", this.element); |
| this._createToolbarItems(); |
| |
| var timelinePane = new WebInspector.VBox(); |
| timelinePane.show(this.element); |
| var topPaneElement = timelinePane.element.createChild("div", "hbox"); |
| topPaneElement.id = "timeline-overview-panel"; |
| |
| // Create top overview component. |
| this._overviewPane = new WebInspector.TimelineOverviewPane("timeline"); |
| this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this)); |
| this._overviewPane.show(topPaneElement); |
| this._statusPaneContainer = timelinePane.element.createChild("div", "status-pane-container fill"); |
| |
| this._createFileSelector(); |
| |
| WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.PageReloadRequested, this._pageReloadRequested, this); |
| WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.Load, this._loadEventFired, this); |
| |
| // Create top level properties splitter. |
| this._detailsSplitWidget = new WebInspector.SplitWidget(false, true, "timelinePanelDetailsSplitViewState"); |
| this._detailsSplitWidget.element.classList.add("timeline-details-split"); |
| this._detailsView = new WebInspector.TimelineDetailsView(this._model, this._filters, this); |
| this._detailsSplitWidget.installResizer(this._detailsView.headerElement()); |
| this._detailsSplitWidget.setSidebarWidget(this._detailsView); |
| |
| this._searchableView = new WebInspector.SearchableView(this); |
| this._searchableView.setMinimumSize(0, 100); |
| this._searchableView.element.classList.add("searchable-view"); |
| this._detailsSplitWidget.setMainWidget(this._searchableView); |
| |
| this._stackView = new WebInspector.StackView(false); |
| this._stackView.element.classList.add("timeline-view-stack"); |
| |
| this._stackView.show(this._searchableView.element); |
| this._onModeChanged(); |
| |
| this._detailsSplitWidget.show(timelinePane.element); |
| this._detailsSplitWidget.hideSidebar(); |
| WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged, this); |
| this._showRecordingHelpMessage(); |
| |
| /** @type {!WebInspector.TracingModel.Event}|undefined */ |
| this._selectedSearchResult; |
| /** @type {!Array<!WebInspector.TracingModel.Event>}|undefined */ |
| this._searchResults; |
| } |
| |
| /** |
| * @enum {string} |
| */ |
| WebInspector.TimelinePanel.Perspectives = { |
| Load: "Load", |
| Responsiveness: "Responsiveness", |
| Custom: "Custom" |
| } |
| |
| /** |
| * @enum {string} |
| */ |
| WebInspector.TimelinePanel.DetailsTab = { |
| Details: "Details", |
| Events: "Events", |
| CallTree: "CallTree", |
| BottomUp: "BottomUp", |
| PaintProfiler: "PaintProfiler", |
| LayerViewer: "LayerViewer" |
| } |
| |
| /** |
| * @enum {symbol} |
| */ |
| WebInspector.TimelinePanel.State = { |
| Idle: Symbol("Idle"), |
| StartPending: Symbol("StartPending"), |
| Recording: Symbol("Recording"), |
| StopPending: Symbol("StopPending"), |
| Loading: Symbol("Loading") |
| } |
| |
| // Define row and header height, should be in sync with styles for timeline graphs. |
| WebInspector.TimelinePanel.rowHeight = 18; |
| WebInspector.TimelinePanel.headerHeight = 20; |
| |
| WebInspector.TimelinePanel.prototype = { |
| /** |
| * @override |
| * @return {?WebInspector.SearchableView} |
| */ |
| searchableView: function() |
| { |
| return this._searchableView; |
| }, |
| |
| wasShown: function() |
| { |
| WebInspector.context.setFlavor(WebInspector.TimelinePanel, this); |
| }, |
| |
| willHide: function() |
| { |
| WebInspector.context.setFlavor(WebInspector.TimelinePanel, null); |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| windowStartTime: function() |
| { |
| if (this._windowStartTime) |
| return this._windowStartTime; |
| return this._model.minimumRecordTime(); |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| windowEndTime: function() |
| { |
| if (this._windowEndTime < Infinity) |
| return this._windowEndTime; |
| return this._model.maximumRecordTime() || Infinity; |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _onWindowChanged: function(event) |
| { |
| this._windowStartTime = event.data.startTime; |
| this._windowEndTime = event.data.endTime; |
| |
| for (var i = 0; i < this._currentViews.length; ++i) |
| this._currentViews[i].setWindowTimes(this._windowStartTime, this._windowEndTime); |
| |
| if (!this._selection || this._selection.type() === WebInspector.TimelineSelection.Type.Range) |
| this.select(null); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _onOverviewSelectionChanged: function(event) |
| { |
| var selection = /** @type {!WebInspector.TimelineSelection} */ (event.data); |
| this.select(selection); |
| }, |
| |
| /** |
| * @override |
| * @param {number} windowStartTime |
| * @param {number} windowEndTime |
| */ |
| requestWindowTimes: function(windowStartTime, windowEndTime) |
| { |
| this._overviewPane.requestWindowTimes(windowStartTime, windowEndTime); |
| }, |
| |
| /** |
| * @return {!WebInspector.Widget} |
| */ |
| _layersView: function() |
| { |
| if (this._lazyLayersView) |
| return this._lazyLayersView; |
| this._lazyLayersView = new WebInspector.TimelineLayersView(this._model, showPaintEventDetails.bind(this)); |
| return this._lazyLayersView; |
| |
| /** |
| * @param {!WebInspector.TracingModel.Event} event |
| * @this {WebInspector.TimelinePanel} |
| */ |
| function showPaintEventDetails(event) |
| { |
| this._showEventInPaintProfiler(event, true); |
| this._detailsView.selectTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler, true); |
| } |
| }, |
| |
| _paintProfilerView: function() |
| { |
| if (this._lazyPaintProfilerView) |
| return this._lazyPaintProfilerView; |
| this._lazyPaintProfilerView = new WebInspector.TimelinePaintProfilerView(this._frameModel); |
| return this._lazyPaintProfilerView; |
| }, |
| |
| /** |
| * @param {!WebInspector.TimelineModeView} modeView |
| */ |
| _addModeView: function(modeView) |
| { |
| modeView.setWindowTimes(this.windowStartTime(), this.windowEndTime()); |
| modeView.refreshRecords(); |
| var splitWidget = this._stackView.appendView(modeView.view(), "timelinePanelTimelineStackSplitViewState", undefined, 112); |
| var resizer = modeView.resizerElement(); |
| if (splitWidget && resizer) { |
| splitWidget.hideDefaultResizer(); |
| splitWidget.installResizer(resizer); |
| } |
| this._currentViews.push(modeView); |
| }, |
| |
| _removeAllModeViews: function() |
| { |
| this._currentViews.forEach(view => view.dispose()); |
| this._currentViews = []; |
| this._stackView.detachChildWidgets(); |
| }, |
| |
| /** |
| * @param {!WebInspector.TimelinePanel.State} state |
| */ |
| _setState: function(state) |
| { |
| this._state = state; |
| this._updateTimelineControls(); |
| }, |
| |
| /** |
| * @param {string} name |
| * @param {!WebInspector.Setting} setting |
| * @param {string} tooltip |
| * @return {!WebInspector.ToolbarItem} |
| */ |
| _createSettingCheckbox: function(name, setting, tooltip) |
| { |
| if (!this._recordingOptionUIControls) |
| this._recordingOptionUIControls = []; |
| var checkboxItem = new WebInspector.ToolbarCheckbox(name, tooltip, setting); |
| this._recordingOptionUIControls.push(checkboxItem); |
| return checkboxItem; |
| }, |
| |
| _createToolbarItems: function() |
| { |
| this._panelToolbar.removeToolbarItems(); |
| |
| var perspectiveSetting = WebInspector.settings.createSetting("timelinePerspective", WebInspector.TimelinePanel.Perspectives.Load); |
| if (Runtime.experiments.isEnabled("timelineRecordingPerspectives")) { |
| /** |
| * @this {!WebInspector.TimelinePanel} |
| */ |
| function onPerspectiveChanged() |
| { |
| perspectiveSetting.set(perspectiveCombobox.selectElement().value); |
| this._createToolbarItems(); |
| } |
| |
| /** |
| * @param {string} id |
| * @param {string} title |
| */ |
| function addPerspectiveOption(id, title) |
| { |
| var option = perspectiveCombobox.createOption(title, "", id); |
| perspectiveCombobox.addOption(option); |
| if (id === perspectiveSetting.get()) |
| perspectiveCombobox.select(option); |
| } |
| |
| var perspectiveCombobox = new WebInspector.ToolbarComboBox(onPerspectiveChanged.bind(this)); |
| addPerspectiveOption(WebInspector.TimelinePanel.Perspectives.Load, WebInspector.UIString("Page Load")); |
| addPerspectiveOption(WebInspector.TimelinePanel.Perspectives.Responsiveness, WebInspector.UIString("Responsiveness")); |
| addPerspectiveOption(WebInspector.TimelinePanel.Perspectives.Custom, WebInspector.UIString("Custom")); |
| this._panelToolbar.appendToolbarItem(perspectiveCombobox); |
| |
| switch (perspectiveSetting.get()) { |
| case WebInspector.TimelinePanel.Perspectives.Load: |
| this._captureNetworkSetting.set(true); |
| this._captureJSProfileSetting.set(true); |
| this._captureMemorySetting.set(false); |
| this._captureLayersAndPicturesSetting.set(false); |
| this._captureFilmStripSetting.set(true); |
| break; |
| case WebInspector.TimelinePanel.Perspectives.Responsiveness: |
| this._captureNetworkSetting.set(true); |
| this._captureJSProfileSetting.set(true); |
| this._captureMemorySetting.set(false); |
| this._captureLayersAndPicturesSetting.set(false); |
| this._captureFilmStripSetting.set(false); |
| break; |
| } |
| } |
| if (Runtime.experiments.isEnabled("timelineRecordingPerspectives") && perspectiveSetting.get() === WebInspector.TimelinePanel.Perspectives.Load) { |
| this._reloadButton = new WebInspector.ToolbarButton(WebInspector.UIString("Record & Reload"), "refresh-toolbar-item"); |
| this._reloadButton.addEventListener("click", () => WebInspector.targetManager.reloadPage()); |
| this._panelToolbar.appendToolbarItem(this._reloadButton); |
| } else { |
| this._panelToolbar.appendToolbarItem(WebInspector.Toolbar.createActionButton(this._toggleRecordAction)); |
| } |
| |
| this._updateTimelineControls(); |
| var clearButton = new WebInspector.ToolbarButton(WebInspector.UIString("Clear recording"), "clear-toolbar-item"); |
| clearButton.addEventListener("click", this._clear, this); |
| this._panelToolbar.appendToolbarItem(clearButton); |
| |
| this._panelToolbar.appendSeparator(); |
| |
| this._panelToolbar.appendText(WebInspector.UIString("Capture:")); |
| |
| var screenshotCheckbox = this._createSettingCheckbox( |
| WebInspector.UIString("Screenshots"), this._captureFilmStripSetting, WebInspector.UIString("Capture screenshots while recording. (Has small performance overhead)")); |
| |
| if (!Runtime.experiments.isEnabled("timelineRecordingPerspectives") || perspectiveSetting.get() === WebInspector.TimelinePanel.Perspectives.Custom) { |
| this._panelToolbar.appendToolbarItem(this._createSettingCheckbox( |
| WebInspector.UIString("Network"), this._captureNetworkSetting, WebInspector.UIString("Show network requests information"))); |
| this._panelToolbar.appendToolbarItem(this._createSettingCheckbox( |
| WebInspector.UIString("JS Profile"), this._captureJSProfileSetting, WebInspector.UIString("Capture JavaScript stacks with sampling profiler. (Has small performance overhead)"))); |
| this._panelToolbar.appendToolbarItem(screenshotCheckbox); |
| this._panelToolbar.appendToolbarItem(this._createSettingCheckbox( |
| WebInspector.UIString("Memory"), this._captureMemorySetting, WebInspector.UIString("Capture memory information on every timeline event."))); |
| this._panelToolbar.appendToolbarItem(this._createSettingCheckbox( |
| WebInspector.UIString("Paint"), this._captureLayersAndPicturesSetting, WebInspector.UIString("Capture graphics layer positions and rasterization draw calls. (Has large performance overhead)"))); |
| } else { |
| this._panelToolbar.appendToolbarItem(screenshotCheckbox); |
| } |
| |
| this._captureNetworkSetting.addChangeListener(this._onNetworkChanged, this); |
| this._captureMemorySetting.addChangeListener(this._onModeChanged, this); |
| this._captureFilmStripSetting.addChangeListener(this._onModeChanged, this); |
| |
| this._panelToolbar.appendSeparator(); |
| var garbageCollectButton = new WebInspector.ToolbarButton(WebInspector.UIString("Collect garbage"), "garbage-collect-toolbar-item"); |
| garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this); |
| this._panelToolbar.appendToolbarItem(garbageCollectButton); |
| |
| if (Runtime.experiments.isEnabled("cpuThrottling")) { |
| this._panelToolbar.appendSeparator(); |
| this._cpuThrottlingCombobox = new WebInspector.ToolbarComboBox(this._onCPUThrottlingChanged.bind(this)); |
| this._panelToolbar.appendToolbarItem(this._cpuThrottlingCombobox); |
| this._populateCPUThrottingCombobox(); |
| } |
| }, |
| |
| _populateCPUThrottingCombobox: function() |
| { |
| var cpuThrottlingCombobox = this._cpuThrottlingCombobox; |
| cpuThrottlingCombobox.removeOptions(); |
| var currentRate = this._cpuThrottlingManager.rate(); |
| var hasSelection = false; |
| /** |
| * @param {string} name |
| * @param {number} value |
| */ |
| function addGroupingOption(name, value) |
| { |
| var option = cpuThrottlingCombobox.createOption(name, "", String(value)); |
| cpuThrottlingCombobox.addOption(option); |
| if (hasSelection || (value && value !== currentRate)) |
| return; |
| cpuThrottlingCombobox.select(option); |
| hasSelection = true; |
| } |
| var predefinedRates = new Map([ |
| [1, WebInspector.UIString("No CPU throttling")], |
| [2, WebInspector.UIString("High end device (2\xD7 slowdown)")], |
| [5, WebInspector.UIString("Low end device (5\xD7 slowdown)")] |
| ]); |
| for (var rate of predefinedRates) |
| addGroupingOption(rate[1], rate[0]); |
| var customRateText = predefinedRates.has(currentRate) |
| ? WebInspector.UIString("Custom rate\u2026") |
| : WebInspector.UIString("Custom rate (%d\xD7 slowdown)", currentRate); |
| addGroupingOption(customRateText, 0); |
| }, |
| |
| _prepareToLoadTimeline: function() |
| { |
| console.assert(this._state === WebInspector.TimelinePanel.State.Idle); |
| this._setState(WebInspector.TimelinePanel.State.Loading); |
| }, |
| |
| _createFileSelector: function() |
| { |
| if (this._fileSelectorElement) |
| this._fileSelectorElement.remove(); |
| this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this)); |
| this.element.appendChild(this._fileSelectorElement); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _contextMenu: function(event) |
| { |
| var contextMenu = new WebInspector.ContextMenu(event); |
| contextMenu.appendItemsAtLocation("timelineMenu"); |
| contextMenu.show(); |
| }, |
| |
| /** |
| * @return {boolean} |
| */ |
| _saveToFile: function() |
| { |
| if (this._state !== WebInspector.TimelinePanel.State.Idle) |
| return true; |
| if (this._model.isEmpty()) |
| return true; |
| |
| var now = new Date(); |
| var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json"; |
| var stream = new WebInspector.FileOutputStream(); |
| |
| /** |
| * @param {boolean} accepted |
| * @this {WebInspector.TimelinePanel} |
| */ |
| function callback(accepted) |
| { |
| if (!accepted) |
| return; |
| var saver = new WebInspector.TracingTimelineSaver(); |
| this._tracingModelBackingStorage.writeToStream(stream, saver); |
| } |
| stream.open(fileName, callback.bind(this)); |
| return true; |
| }, |
| |
| /** |
| * @return {boolean} |
| */ |
| _selectFileToLoad: function() |
| { |
| this._fileSelectorElement.click(); |
| return true; |
| }, |
| |
| /** |
| * @param {!File} file |
| */ |
| _loadFromFile: function(file) |
| { |
| if (this._state !== WebInspector.TimelinePanel.State.Idle) |
| return; |
| this._prepareToLoadTimeline(); |
| this._loader = WebInspector.TimelineLoader.loadFromFile(this._tracingModel, file, this); |
| this._createFileSelector(); |
| }, |
| |
| /** |
| * @param {string} url |
| */ |
| _loadFromURL: function(url) |
| { |
| if (this._state !== WebInspector.TimelinePanel.State.Idle) |
| return; |
| this._prepareToLoadTimeline(); |
| this._loader = WebInspector.TimelineLoader.loadFromURL(this._tracingModel, url, this); |
| }, |
| |
| _refreshViews: function() |
| { |
| for (var i = 0; i < this._currentViews.length; ++i) { |
| var view = this._currentViews[i]; |
| view.refreshRecords(); |
| } |
| this._updateSelectionDetails(); |
| }, |
| |
| _onModeChanged: function() |
| { |
| // Set up overview controls. |
| this._overviewControls = []; |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.Responsiveness(this._model, this._frameModel)); |
| if (Runtime.experiments.isEnabled("inputEventsOnTimelineOverview")) |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.Input(this._model)); |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.Frames(this._model, this._frameModel)); |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.CPUActivity(this._model)); |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.Network(this._model)); |
| if (this._captureFilmStripSetting.get()) |
| this._overviewControls.push(new WebInspector.TimelineFilmStripOverview(this._model, this._tracingModel)); |
| if (this._captureMemorySetting.get()) |
| this._overviewControls.push(new WebInspector.TimelineEventOverview.Memory(this._model)); |
| this._overviewPane.setOverviewControls(this._overviewControls); |
| |
| // Set up the main view. |
| this._removeAllModeViews(); |
| this._flameChart = new WebInspector.TimelineFlameChartView(this, this._model, this._frameModel, this._irModel, this._filters); |
| this._flameChart.enableNetworkPane(this._captureNetworkSetting.get()); |
| this._addModeView(this._flameChart); |
| |
| if (this._captureMemorySetting.get()) |
| this._addModeView(new WebInspector.MemoryCountersGraph(this, this._model, [WebInspector.TimelineUIUtils.visibleEventsFilter()])); |
| |
| this.doResize(); |
| this.select(null); |
| }, |
| |
| _onNetworkChanged: function() |
| { |
| if (this._flameChart) |
| this._flameChart.enableNetworkPane(this._captureNetworkSetting.get(), true); |
| }, |
| |
| _onCPUThrottlingChanged: function() |
| { |
| if (!this._cpuThrottlingManager) |
| return; |
| var value = this._cpuThrottlingCombobox.selectedOption().value; |
| var isLastOption = this._cpuThrottlingCombobox.selectedIndex() === this._cpuThrottlingCombobox.size() - 1; |
| this._populateCPUThrottingCombobox(); |
| var resultPromise = isLastOption |
| ? WebInspector.TimelinePanel.CustomCPUThrottlingRateDialog.show(this._cpuThrottlingCombobox.element) |
| : Promise.resolve(value); |
| resultPromise.then(text => { |
| this._cpuThrottlingManager.setRate(Number.parseFloat(text)); |
| this._populateCPUThrottingCombobox(); |
| }); |
| }, |
| |
| /** |
| * @param {boolean} enabled |
| */ |
| _setUIControlsEnabled: function(enabled) |
| { |
| /** |
| * @param {!WebInspector.ToolbarButton} toolbarButton |
| */ |
| function handler(toolbarButton) |
| { |
| toolbarButton.setEnabled(enabled); |
| } |
| this._recordingOptionUIControls.forEach(handler); |
| }, |
| |
| /** |
| * @param {boolean} userInitiated |
| */ |
| _startRecording: function(userInitiated) |
| { |
| console.assert(!this._statusPane, "Status pane is already opened."); |
| var mainTarget = WebInspector.targetManager.mainTarget(); |
| if (!mainTarget) |
| return; |
| this._setState(WebInspector.TimelinePanel.State.StartPending); |
| this._showRecordingStarted(); |
| |
| this._autoRecordGeneration = userInitiated ? null : Symbol("Generation"); |
| this._controller = new WebInspector.TimelineController(mainTarget, this, this._tracingModel); |
| this._controller.startRecording(true, this._captureJSProfileSetting.get(), this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting.get(), this._captureFilmStripSetting && this._captureFilmStripSetting.get()); |
| |
| for (var i = 0; i < this._overviewControls.length; ++i) |
| this._overviewControls[i].timelineStarted(); |
| |
| if (userInitiated) |
| WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.TimelineStarted); |
| this._setUIControlsEnabled(false); |
| this._hideRecordingHelpMessage(); |
| }, |
| |
| _stopRecording: function() |
| { |
| if (this._statusPane) { |
| this._statusPane.finish(); |
| this._statusPane.updateStatus(WebInspector.UIString("Stopping timeline\u2026")); |
| this._statusPane.updateProgressBar(WebInspector.UIString("Received"), 0); |
| } |
| this._setState(WebInspector.TimelinePanel.State.StopPending); |
| this._autoRecordGeneration = null; |
| this._controller.stopRecording(); |
| this._controller = null; |
| this._setUIControlsEnabled(true); |
| }, |
| |
| _onSuspendStateChanged: function() |
| { |
| this._updateTimelineControls(); |
| }, |
| |
| _updateTimelineControls: function() |
| { |
| var state = WebInspector.TimelinePanel.State; |
| this._toggleRecordAction.setToggled(this._state === state.Recording); |
| this._toggleRecordAction.setEnabled(this._state === state.Recording || this._state === state.Idle); |
| this._panelToolbar.setEnabled(this._state !== state.Loading); |
| this._dropTarget.setEnabled(this._state === state.Idle); |
| }, |
| |
| _toggleRecording: function() |
| { |
| if (this._state === WebInspector.TimelinePanel.State.Idle) |
| this._startRecording(true); |
| else if (this._state === WebInspector.TimelinePanel.State.Recording) |
| this._stopRecording(); |
| }, |
| |
| _garbageCollectButtonClicked: function() |
| { |
| var targets = WebInspector.targetManager.targets(); |
| for (var i = 0; i < targets.length; ++i) |
| targets[i].heapProfilerAgent().collectGarbage(); |
| }, |
| |
| _clear: function() |
| { |
| WebInspector.LineLevelProfile.instance().reset(); |
| this._tracingModel.reset(); |
| this._model.reset(); |
| this._showRecordingHelpMessage(); |
| |
| this.requestWindowTimes(0, Infinity); |
| delete this._selection; |
| this._frameModel.reset(); |
| this._overviewPane.reset(); |
| for (var i = 0; i < this._currentViews.length; ++i) |
| this._currentViews[i].reset(); |
| for (var i = 0; i < this._overviewControls.length; ++i) |
| this._overviewControls[i].reset(); |
| this.select(null); |
| delete this._filmStripModel; |
| this._detailsSplitWidget.hideSidebar(); |
| }, |
| |
| /** |
| * @override |
| */ |
| recordingStarted: function() |
| { |
| this._clear(); |
| this._setState(WebInspector.TimelinePanel.State.Recording); |
| this._showRecordingStarted(); |
| this._statusPane.updateStatus(WebInspector.UIString("Recording\u2026")); |
| this._statusPane.updateProgressBar(WebInspector.UIString("Buffer usage"), 0) |
| this._statusPane.startTimer(); |
| this._hideRecordingHelpMessage(); |
| }, |
| |
| /** |
| * @override |
| * @param {number} usage |
| */ |
| recordingProgress: function(usage) |
| { |
| this._statusPane.updateProgressBar(WebInspector.UIString("Buffer usage"), usage * 100); |
| }, |
| |
| _showRecordingHelpMessage: function() |
| { |
| /** |
| * @param {string} tagName |
| * @param {string} contents |
| * @return {!Element} |
| */ |
| function encloseWithTag(tagName, contents) |
| { |
| var e = createElement(tagName); |
| e.textContent = contents; |
| return e; |
| } |
| |
| var recordNode = encloseWithTag("b", WebInspector.shortcutRegistry.shortcutDescriptorsForAction("timeline.toggle-recording")[0].name); |
| var reloadNode = encloseWithTag("b", WebInspector.shortcutRegistry.shortcutDescriptorsForAction("main.reload")[0].name); |
| var navigateNode = encloseWithTag("b", WebInspector.UIString("WASD")); |
| var hintText = createElementWithClass("div"); |
| hintText.appendChild(WebInspector.formatLocalized("To capture a new timeline, click the record toolbar button or hit %s.", [recordNode])); |
| hintText.createChild("br"); |
| hintText.appendChild(WebInspector.formatLocalized("To evaluate page load performance, hit %s to record the reload.", [reloadNode])); |
| hintText.createChild("p"); |
| hintText.appendChild(WebInspector.formatLocalized("After recording, select an area of interest in the overview by dragging.", [])); |
| hintText.createChild("br"); |
| hintText.appendChild(WebInspector.formatLocalized("Then, zoom and pan the timeline with the mousewheel and %s keys.", [navigateNode])); |
| this._hideRecordingHelpMessage(); |
| this._helpMessageElement = this._searchableView.element.createChild("div", "banner timeline-status-pane"); |
| this._helpMessageElement.appendChild(hintText); |
| }, |
| |
| _hideRecordingHelpMessage: function() |
| { |
| if (this._helpMessageElement) |
| this._helpMessageElement.remove(); |
| delete this._helpMessageElement; |
| }, |
| |
| /** |
| * @override |
| */ |
| loadingStarted: function() |
| { |
| this._hideRecordingHelpMessage(); |
| |
| if (this._statusPane) |
| this._statusPane.hide(); |
| this._statusPane = new WebInspector.TimelinePanel.StatusPane(false, this._cancelLoading.bind(this)); |
| this._statusPane.showPane(this._statusPaneContainer); |
| this._statusPane.updateStatus(WebInspector.UIString("Loading timeline\u2026")); |
| // FIXME: make loading from backend cancelable as well. |
| if (!this._loader) |
| this._statusPane.finish(); |
| this.loadingProgress(0); |
| }, |
| |
| /** |
| * @override |
| * @param {number=} progress |
| */ |
| loadingProgress: function(progress) |
| { |
| if (typeof progress === "number") |
| this._statusPane.updateProgressBar(WebInspector.UIString("Received"), progress * 100); |
| }, |
| |
| /** |
| * @override |
| * @param {boolean} success |
| */ |
| loadingComplete: function(success) |
| { |
| var loadedFromFile = !!this._loader; |
| delete this._loader; |
| this._setState(WebInspector.TimelinePanel.State.Idle); |
| |
| if (!success) { |
| this._statusPane.hide(); |
| delete this._statusPane; |
| this._clear(); |
| return; |
| } |
| |
| if (this._statusPane) |
| this._statusPane.updateStatus(WebInspector.UIString("Processing timeline\u2026")); |
| this._model.setEvents(this._tracingModel, loadedFromFile); |
| this._frameModel.reset(); |
| this._frameModel.addTraceEvents(WebInspector.targetManager.mainTarget(), this._model.inspectedTargetEvents(), this._model.sessionId() || ""); |
| |
| var groups = WebInspector.TimelineModel.AsyncEventGroup; |
| var asyncEventsByGroup = this._model.mainThreadAsyncEvents(); |
| this._irModel.populate(asyncEventsByGroup.get(groups.input), asyncEventsByGroup.get(groups.animation)); |
| this._model.cpuProfiles().forEach(profile => WebInspector.LineLevelProfile.instance().appendCPUProfile(profile)); |
| if (this._statusPane) |
| this._statusPane.hide(); |
| delete this._statusPane; |
| this._overviewPane.reset(); |
| this._overviewPane.setBounds(this._model.minimumRecordTime(), this._model.maximumRecordTime()); |
| this._setAutoWindowTimes(); |
| this._refreshViews(); |
| for (var i = 0; i < this._overviewControls.length; ++i) |
| this._overviewControls[i].timelineStopped(); |
| this._setMarkers(); |
| this._overviewPane.scheduleUpdate(); |
| this._updateSearchHighlight(false, true); |
| this._detailsSplitWidget.showBoth(); |
| }, |
| |
| _showRecordingStarted: function() |
| { |
| if (this._statusPane) |
| return; |
| this._statusPane = new WebInspector.TimelinePanel.StatusPane(true, this._stopRecording.bind(this)); |
| this._statusPane.showPane(this._statusPaneContainer); |
| this._statusPane.updateStatus(WebInspector.UIString("Initializing recording\u2026")); |
| }, |
| |
| _cancelLoading: function() |
| { |
| if (this._loader) |
| this._loader.cancel(); |
| }, |
| |
| _setMarkers: function() |
| { |
| var markers = new Map(); |
| var recordTypes = WebInspector.TimelineModel.RecordType; |
| var zeroTime = this._model.minimumRecordTime(); |
| for (var record of this._model.eventDividerRecords()) { |
| if (record.type() === recordTypes.TimeStamp || record.type() === recordTypes.ConsoleTime) |
| continue; |
| markers.set(record.startTime(), WebInspector.TimelineUIUtils.createDividerForRecord(record, zeroTime, 0)); |
| } |
| this._overviewPane.setMarkers(markers); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _pageReloadRequested: function(event) |
| { |
| if (this._state !== WebInspector.TimelinePanel.State.Idle || !this.isShowing()) |
| return; |
| this._startRecording(false); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _loadEventFired: function(event) |
| { |
| if (this._state !== WebInspector.TimelinePanel.State.Recording || !this._autoRecordGeneration) |
| return; |
| setTimeout(stopRecordingOnReload.bind(this, this._autoRecordGeneration), this._millisecondsToRecordAfterLoadEvent); |
| |
| /** |
| * @this {WebInspector.TimelinePanel} |
| * @param {!Object} recordGeneration |
| */ |
| function stopRecordingOnReload(recordGeneration) |
| { |
| // Check if we're still in the same recording session. |
| if (this._state !== WebInspector.TimelinePanel.State.Recording || this._autoRecordGeneration !== recordGeneration) |
| return; |
| this._stopRecording(); |
| } |
| }, |
| |
| // WebInspector.Searchable implementation |
| |
| /** |
| * @override |
| */ |
| jumpToNextSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : -1; |
| this._jumpToSearchResult(index + 1); |
| }, |
| |
| /** |
| * @override |
| */ |
| jumpToPreviousSearchResult: function() |
| { |
| if (!this._searchResults || !this._searchResults.length) |
| return; |
| var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : 0; |
| this._jumpToSearchResult(index - 1); |
| }, |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| supportsCaseSensitiveSearch: function() |
| { |
| return false; |
| }, |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| supportsRegexSearch: function() |
| { |
| return false; |
| }, |
| |
| /** |
| * @param {number} index |
| */ |
| _jumpToSearchResult: function(index) |
| { |
| this._selectSearchResult((index + this._searchResults.length) % this._searchResults.length); |
| this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, true); |
| }, |
| |
| /** |
| * @param {number} index |
| */ |
| _selectSearchResult: function(index) |
| { |
| this._selectedSearchResult = this._searchResults[index]; |
| this._searchableView.updateCurrentMatchIndex(index); |
| }, |
| |
| _clearHighlight: function() |
| { |
| this._currentViews[0].highlightSearchResult(null); |
| }, |
| |
| /** |
| * @param {boolean} revealRecord |
| * @param {boolean} shouldJump |
| * @param {boolean=} jumpBackwards |
| */ |
| _updateSearchHighlight: function(revealRecord, shouldJump, jumpBackwards) |
| { |
| if (!this._searchRegex) { |
| this._clearHighlight(); |
| return; |
| } |
| |
| if (!this._searchResults) |
| this._updateSearchResults(shouldJump, jumpBackwards); |
| this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, revealRecord); |
| }, |
| |
| /** |
| * @param {boolean} shouldJump |
| * @param {boolean=} jumpBackwards |
| */ |
| _updateSearchResults: function(shouldJump, jumpBackwards) |
| { |
| if (!this._searchRegex) |
| return; |
| |
| // FIXME: search on all threads. |
| var events = this._model.mainThreadEvents(); |
| var filters = this._filters.concat([new WebInspector.TimelineTextFilter(this._searchRegex)]); |
| var matches = []; |
| for (var index = events.lowerBound(this._windowStartTime, (time, event) => time - event.startTime); index < events.length; ++index) { |
| var event = events[index]; |
| if (event.startTime > this._windowEndTime) |
| break; |
| if (WebInspector.TimelineModel.isVisible(filters, event)) |
| matches.push(event); |
| } |
| |
| var matchesCount = matches.length; |
| if (matchesCount) { |
| this._searchResults = matches; |
| this._searchableView.updateSearchMatchesCount(matchesCount); |
| |
| var selectedIndex = matches.indexOf(this._selectedSearchResult); |
| if (shouldJump && selectedIndex === -1) |
| selectedIndex = jumpBackwards ? this._searchResults.length - 1 : 0; |
| this._selectSearchResult(selectedIndex); |
| } else { |
| this._searchableView.updateSearchMatchesCount(0); |
| delete this._selectedSearchResult; |
| } |
| }, |
| |
| /** |
| * @override |
| */ |
| searchCanceled: function() |
| { |
| this._clearHighlight(); |
| delete this._searchResults; |
| delete this._selectedSearchResult; |
| delete this._searchRegex; |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.SearchableView.SearchConfig} searchConfig |
| * @param {boolean} shouldJump |
| * @param {boolean=} jumpBackwards |
| */ |
| performSearch: function(searchConfig, shouldJump, jumpBackwards) |
| { |
| var query = searchConfig.query; |
| this._searchRegex = createPlainTextSearchRegex(query, "i"); |
| delete this._searchResults; |
| this._updateSearchHighlight(true, shouldJump, jumpBackwards); |
| }, |
| |
| _updateSelectionDetails: function() |
| { |
| switch (this._selection.type()) { |
| case WebInspector.TimelineSelection.Type.TraceEvent: |
| var event = /** @type {!WebInspector.TracingModel.Event} */ (this._selection.object()); |
| WebInspector.TimelineUIUtils.buildTraceEventDetails(event, this._model, this._detailsLinkifier, true, this._appendDetailsTabsForTraceEventAndShowDetails.bind(this, event)); |
| break; |
| case WebInspector.TimelineSelection.Type.Frame: |
| var frame = /** @type {!WebInspector.TimelineFrame} */ (this._selection.object()); |
| if (!this._filmStripModel) |
| this._filmStripModel = new WebInspector.FilmStripModel(this._tracingModel); |
| var screenshotTime = frame.idle ? frame.startTime : frame.endTime; // For idle frames, look at the state at the beginning of the frame. |
| var filmStripFrame = this._filmStripModel && this._filmStripModel.frameByTimestamp(screenshotTime); |
| if (filmStripFrame && filmStripFrame.timestamp - frame.endTime > 10) |
| filmStripFrame = null; |
| this.showInDetails(WebInspector.TimelineUIUtils.generateDetailsContentForFrame(this._frameModel, frame, filmStripFrame)); |
| if (frame.layerTree) { |
| var layersView = this._layersView(); |
| layersView.showLayerTree(frame.layerTree, frame.paints); |
| if (!this._detailsView.hasTab(WebInspector.TimelinePanel.DetailsTab.LayerViewer)) |
| this._detailsView.appendTab(WebInspector.TimelinePanel.DetailsTab.LayerViewer, WebInspector.UIString("Layers"), layersView); |
| } |
| break; |
| case WebInspector.TimelineSelection.Type.NetworkRequest: |
| var request = /** @type {!WebInspector.TimelineModel.NetworkRequest} */ (this._selection.object()); |
| WebInspector.TimelineUIUtils.buildNetworkRequestDetails(request, this._model, this._detailsLinkifier) |
| .then(this.showInDetails.bind(this)); |
| break; |
| case WebInspector.TimelineSelection.Type.Range: |
| this._updateSelectedRangeStats(this._selection._startTime, this._selection._endTime); |
| break; |
| } |
| |
| this._detailsView.updateContents(this._selection); |
| }, |
| |
| /** |
| * @param {!WebInspector.TimelineSelection} selection |
| * @return {?WebInspector.TimelineFrame} |
| */ |
| _frameForSelection: function(selection) |
| { |
| switch (selection.type()) { |
| case WebInspector.TimelineSelection.Type.Frame: |
| return /** @type {!WebInspector.TimelineFrame} */ (selection.object()); |
| case WebInspector.TimelineSelection.Type.Range: |
| return null; |
| case WebInspector.TimelineSelection.Type.TraceEvent: |
| return this._frameModel.filteredFrames(selection._endTime, selection._endTime)[0]; |
| default: |
| console.assert(false, "Should never be reached"); |
| return null; |
| } |
| }, |
| |
| /** |
| * @param {number} offset |
| */ |
| _jumpToFrame: function(offset) |
| { |
| var currentFrame = this._frameForSelection(this._selection); |
| if (!currentFrame) |
| return; |
| var frames = this._frameModel.frames(); |
| var index = frames.indexOf(currentFrame); |
| console.assert(index >= 0, "Can't find current frame in the frame list"); |
| index = Number.constrain(index + offset, 0, frames.length - 1); |
| var frame = frames[index]; |
| this._revealTimeRange(frame.startTime, frame.endTime); |
| this.select(WebInspector.TimelineSelection.fromFrame(frame)); |
| return true; |
| }, |
| |
| /** |
| * @param {!WebInspector.TracingModel.Event} event |
| * @param {!Node} content |
| */ |
| _appendDetailsTabsForTraceEventAndShowDetails: function(event, content) |
| { |
| this.showInDetails(content); |
| if (event.name === WebInspector.TimelineModel.RecordType.Paint || event.name === WebInspector.TimelineModel.RecordType.RasterTask) |
| this._showEventInPaintProfiler(event); |
| }, |
| |
| /** |
| * @param {!WebInspector.TracingModel.Event} event |
| * @param {boolean=} isCloseable |
| */ |
| _showEventInPaintProfiler: function(event, isCloseable) |
| { |
| var target = WebInspector.targetManager.mainTarget(); |
| if (!target) |
| return; |
| var paintProfilerView = this._paintProfilerView(); |
| var hasProfileData = paintProfilerView.setEvent(target, event); |
| if (!hasProfileData) |
| return; |
| if (!this._detailsView.hasTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler)) |
| this._detailsView.appendTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler, WebInspector.UIString("Paint Profiler"), paintProfilerView, undefined, undefined, isCloseable); |
| }, |
| |
| /** |
| * @param {number} startTime |
| * @param {number} endTime |
| */ |
| _updateSelectedRangeStats: function(startTime, endTime) |
| { |
| this.showInDetails(WebInspector.TimelineUIUtils.buildRangeStats(this._model, startTime, endTime)); |
| }, |
| |
| /** |
| * @override |
| * @param {?WebInspector.TimelineSelection} selection |
| * @param {!WebInspector.TimelinePanel.DetailsTab=} preferredTab |
| */ |
| select: function(selection, preferredTab) |
| { |
| if (!selection) |
| selection = WebInspector.TimelineSelection.fromRange(this._windowStartTime, this._windowEndTime); |
| this._selection = selection; |
| this._detailsLinkifier.reset(); |
| if (preferredTab) |
| this._detailsView.setPreferredTab(preferredTab); |
| |
| for (var view of this._currentViews) |
| view.setSelection(selection); |
| this._updateSelectionDetails(); |
| }, |
| |
| /** |
| * @override |
| * @param {number} time |
| */ |
| selectEntryAtTime: function(time) |
| { |
| var events = this._model.mainThreadEvents(); |
| // Find best match, then backtrack to the first visible entry. |
| for (var index = events.upperBound(time, (time, event) => time - event.startTime) - 1; index >= 0; --index) { |
| var event = events[index]; |
| var endTime = event.endTime || event.startTime; |
| if (WebInspector.TracingModel.isTopLevelEvent(event) && endTime < time) |
| break; |
| if (WebInspector.TimelineModel.isVisible(this._filters, event) && endTime >= time) { |
| this.select(WebInspector.TimelineSelection.fromTraceEvent(event)); |
| return; |
| } |
| } |
| this.select(null); |
| }, |
| |
| /** |
| * @override |
| * @param {?WebInspector.TracingModel.Event} event |
| */ |
| highlightEvent: function(event) |
| { |
| for (var view of this._currentViews) |
| view.highlightEvent(event); |
| }, |
| |
| /** |
| * @param {number} startTime |
| * @param {number} endTime |
| */ |
| _revealTimeRange: function(startTime, endTime) |
| { |
| var timeShift = 0; |
| if (this._windowEndTime < endTime) |
| timeShift = endTime - this._windowEndTime; |
| else if (this._windowStartTime > startTime) |
| timeShift = startTime - this._windowStartTime; |
| if (timeShift) |
| this.requestWindowTimes(this._windowStartTime + timeShift, this._windowEndTime + timeShift); |
| }, |
| |
| /** |
| * @override |
| * @param {!Node} node |
| */ |
| showInDetails: function(node) |
| { |
| this._detailsView.setContent(node); |
| }, |
| |
| /** |
| * @param {!DataTransfer} dataTransfer |
| */ |
| _handleDrop: function(dataTransfer) |
| { |
| var items = dataTransfer.items; |
| if (!items.length) |
| return; |
| var item = items[0]; |
| if (item.kind === "string") { |
| var url = dataTransfer.getData("text/uri-list"); |
| if (new WebInspector.ParsedURL(url).isValid) |
| this._loadFromURL(url); |
| } else if (item.kind === "file") { |
| var entry = items[0].webkitGetAsEntry(); |
| if (!entry.isFile) |
| return; |
| entry.file(this._loadFromFile.bind(this)); |
| } |
| }, |
| |
| _setAutoWindowTimes: function() |
| { |
| var tasks = this._model.mainThreadTasks(); |
| if (!tasks.length) { |
| this.requestWindowTimes(this._tracingModel.minimumRecordTime(), this._tracingModel.maximumRecordTime()); |
| return; |
| } |
| /** |
| * @param {number} startIndex |
| * @param {number} stopIndex |
| * @return {number} |
| */ |
| function findLowUtilizationRegion(startIndex, stopIndex) |
| { |
| var /** @const */ threshold = 0.1; |
| var cutIndex = startIndex; |
| var cutTime = (tasks[cutIndex].startTime() + tasks[cutIndex].endTime()) / 2; |
| var usedTime = 0; |
| var step = Math.sign(stopIndex - startIndex); |
| for (var i = startIndex; i !== stopIndex; i += step) { |
| var task = tasks[i]; |
| var taskTime = (task.startTime() + task.endTime()) / 2; |
| var interval = Math.abs(cutTime - taskTime); |
| if (usedTime < threshold * interval) { |
| cutIndex = i; |
| cutTime = taskTime; |
| usedTime = 0; |
| } |
| usedTime += task.endTime() - task.startTime(); |
| } |
| return cutIndex; |
| } |
| var rightIndex = findLowUtilizationRegion(tasks.length - 1, 0); |
| var leftIndex = findLowUtilizationRegion(0, rightIndex); |
| var leftTime = tasks[leftIndex].startTime(); |
| var rightTime = tasks[rightIndex].endTime(); |
| var span = rightTime - leftTime; |
| var totalSpan = this._tracingModel.maximumRecordTime() - this._tracingModel.minimumRecordTime(); |
| if (span < totalSpan * 0.1) { |
| leftTime = this._tracingModel.minimumRecordTime(); |
| rightTime = this._tracingModel.maximumRecordTime(); |
| } else { |
| leftTime = Math.max(leftTime - 0.05 * span, this._tracingModel.minimumRecordTime()); |
| rightTime = Math.min(rightTime + 0.05 * span, this._tracingModel.maximumRecordTime()); |
| } |
| this.requestWindowTimes(leftTime, rightTime); |
| }, |
| |
| __proto__: WebInspector.Panel.prototype |
| } |
| |
| /** |
| * @interface |
| */ |
| WebInspector.TimelineLifecycleDelegate = function() |
| { |
| } |
| |
| WebInspector.TimelineLifecycleDelegate.prototype = { |
| recordingStarted: function() {}, |
| |
| /** |
| * @param {number} usage |
| */ |
| recordingProgress: function(usage) {}, |
| |
| loadingStarted: function() {}, |
| |
| /** |
| * @param {number=} progress |
| */ |
| loadingProgress: function(progress) {}, |
| |
| /** |
| * @param {boolean} success |
| */ |
| loadingComplete: function(success) {}, |
| }; |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.TabbedPane} |
| * @param {!WebInspector.TimelineModel} timelineModel |
| * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| * @param {!WebInspector.TimelineModeViewDelegate} delegate |
| */ |
| WebInspector.TimelineDetailsView = function(timelineModel, filters, delegate) |
| { |
| WebInspector.TabbedPane.call(this); |
| this.element.classList.add("timeline-details"); |
| |
| var tabIds = WebInspector.TimelinePanel.DetailsTab; |
| this._defaultDetailsWidget = new WebInspector.VBox(); |
| this._defaultDetailsWidget.element.classList.add("timeline-details-view"); |
| this._defaultDetailsContentElement = this._defaultDetailsWidget.element.createChild("div", "timeline-details-view-body vbox"); |
| this.appendTab(tabIds.Details, WebInspector.UIString("Summary"), this._defaultDetailsWidget); |
| this.setPreferredTab(tabIds.Details); |
| |
| /** @type Map<string, WebInspector.TimelineTreeView> */ |
| this._rangeDetailViews = new Map(); |
| |
| var bottomUpView = new WebInspector.BottomUpTimelineTreeView(timelineModel, filters); |
| this.appendTab(tabIds.BottomUp, WebInspector.UIString("Bottom-Up"), bottomUpView); |
| this._rangeDetailViews.set(tabIds.BottomUp, bottomUpView); |
| |
| var callTreeView = new WebInspector.CallTreeTimelineTreeView(timelineModel, filters); |
| this.appendTab(tabIds.CallTree, WebInspector.UIString("Call Tree"), callTreeView); |
| this._rangeDetailViews.set(tabIds.CallTree, callTreeView); |
| |
| var eventsView = new WebInspector.EventsTimelineTreeView(timelineModel, filters, delegate); |
| this.appendTab(tabIds.Events, WebInspector.UIString("Event Log"), eventsView); |
| this._rangeDetailViews.set(tabIds.Events, eventsView); |
| |
| this.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); |
| } |
| |
| WebInspector.TimelineDetailsView.prototype = { |
| /** |
| * @param {!Node} node |
| */ |
| setContent: function(node) |
| { |
| var allTabs = this.otherTabs(WebInspector.TimelinePanel.DetailsTab.Details); |
| for (var i = 0; i < allTabs.length; ++i) { |
| if (!this._rangeDetailViews.has(allTabs[i])) |
| this.closeTab(allTabs[i]); |
| } |
| this._defaultDetailsContentElement.removeChildren(); |
| this._defaultDetailsContentElement.appendChild(node); |
| }, |
| |
| /** |
| * @param {!WebInspector.TimelineSelection} selection |
| */ |
| updateContents: function(selection) |
| { |
| this._selection = selection; |
| var view = this.selectedTabId ? this._rangeDetailViews.get(this.selectedTabId) : null; |
| if (view) |
| view.updateContents(selection); |
| }, |
| |
| /** |
| * @override |
| * @param {string} id |
| * @param {string} tabTitle |
| * @param {!WebInspector.Widget} view |
| * @param {string=} tabTooltip |
| * @param {boolean=} userGesture |
| * @param {boolean=} isCloseable |
| */ |
| appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable) |
| { |
| WebInspector.TabbedPane.prototype.appendTab.call(this, id, tabTitle, view, tabTooltip, userGesture, isCloseable); |
| if (this._preferredTabId !== this.selectedTabId) |
| this.selectTab(id); |
| }, |
| |
| /** |
| * @param {string} tabId |
| */ |
| setPreferredTab: function(tabId) |
| { |
| this._preferredTabId = tabId; |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _tabSelected: function(event) |
| { |
| if (!event.data.isUserGesture) |
| return; |
| this.setPreferredTab(event.data.tabId); |
| this.updateContents(this._selection); |
| }, |
| |
| __proto__: WebInspector.TabbedPane.prototype |
| } |
| |
| /** |
| * @constructor |
| * @param {!WebInspector.TimelineSelection.Type} type |
| * @param {number} startTime |
| * @param {number} endTime |
| * @param {!Object=} object |
| */ |
| WebInspector.TimelineSelection = function(type, startTime, endTime, object) |
| { |
| this._type = type; |
| this._startTime = startTime; |
| this._endTime = endTime; |
| this._object = object || null; |
| } |
| |
| /** |
| * @enum {string} |
| */ |
| WebInspector.TimelineSelection.Type = { |
| Frame: "Frame", |
| NetworkRequest: "NetworkRequest", |
| TraceEvent: "TraceEvent", |
| Range: "Range" |
| }; |
| |
| /** |
| * @param {!WebInspector.TimelineFrame} frame |
| * @return {!WebInspector.TimelineSelection} |
| */ |
| WebInspector.TimelineSelection.fromFrame = function(frame) |
| { |
| return new WebInspector.TimelineSelection( |
| WebInspector.TimelineSelection.Type.Frame, |
| frame.startTime, frame.endTime, |
| frame); |
| } |
| |
| /** |
| * @param {!WebInspector.TimelineModel.NetworkRequest} request |
| * @return {!WebInspector.TimelineSelection} |
| */ |
| WebInspector.TimelineSelection.fromNetworkRequest = function(request) |
| { |
| return new WebInspector.TimelineSelection( |
| WebInspector.TimelineSelection.Type.NetworkRequest, |
| request.startTime, request.endTime || request.startTime, |
| request); |
| } |
| |
| /** |
| * @param {!WebInspector.TracingModel.Event} event |
| * @return {!WebInspector.TimelineSelection} |
| */ |
| WebInspector.TimelineSelection.fromTraceEvent = function(event) |
| { |
| return new WebInspector.TimelineSelection( |
| WebInspector.TimelineSelection.Type.TraceEvent, |
| event.startTime, event.endTime || (event.startTime + 1), |
| event); |
| } |
| |
| /** |
| * @param {number} startTime |
| * @param {number} endTime |
| * @return {!WebInspector.TimelineSelection} |
| */ |
| WebInspector.TimelineSelection.fromRange = function(startTime, endTime) |
| { |
| return new WebInspector.TimelineSelection( |
| WebInspector.TimelineSelection.Type.Range, |
| startTime, endTime); |
| } |
| |
| WebInspector.TimelineSelection.prototype = { |
| /** |
| * @return {!WebInspector.TimelineSelection.Type} |
| */ |
| type: function() |
| { |
| return this._type; |
| }, |
| |
| /** |
| * @return {?Object} |
| */ |
| object: function() |
| { |
| return this._object; |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| startTime: function() |
| { |
| return this._startTime; |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| endTime: function() |
| { |
| return this._endTime; |
| } |
| }; |
| |
| /** |
| * @interface |
| * @extends {WebInspector.EventTarget} |
| */ |
| WebInspector.TimelineModeView = function() |
| { |
| } |
| |
| WebInspector.TimelineModeView.prototype = { |
| /** |
| * @return {!WebInspector.Widget} |
| */ |
| view: function() {}, |
| |
| dispose: function() {}, |
| |
| /** |
| * @return {?Element} |
| */ |
| resizerElement: function() {}, |
| |
| reset: function() {}, |
| |
| refreshRecords: function() {}, |
| |
| /** |
| * @param {?WebInspector.TracingModel.Event} event |
| * @param {string=} regex |
| * @param {boolean=} select |
| */ |
| highlightSearchResult: function(event, regex, select) {}, |
| |
| /** |
| * @param {number} startTime |
| * @param {number} endTime |
| */ |
| setWindowTimes: function(startTime, endTime) {}, |
| |
| /** |
| * @param {?WebInspector.TimelineSelection} selection |
| */ |
| setSelection: function(selection) {}, |
| |
| /** |
| * @param {?WebInspector.TracingModel.Event} event |
| */ |
| highlightEvent: function(event) { } |
| } |
| |
| /** |
| * @interface |
| */ |
| WebInspector.TimelineModeViewDelegate = function() {} |
| |
| WebInspector.TimelineModeViewDelegate.prototype = { |
| /** |
| * @param {number} startTime |
| * @param {number} endTime |
| */ |
| requestWindowTimes: function(startTime, endTime) {}, |
| |
| /** |
| * @param {?WebInspector.TimelineSelection} selection |
| * @param {!WebInspector.TimelinePanel.DetailsTab=} preferredTab |
| */ |
| select: function(selection, preferredTab) {}, |
| |
| /** |
| * @param {number} time |
| */ |
| selectEntryAtTime: function(time) {}, |
| |
| /** |
| * @param {!Node} node |
| */ |
| showInDetails: function(node) {}, |
| |
| /** |
| * @param {?WebInspector.TracingModel.Event} event |
| */ |
| highlightEvent: function(event) {} |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.TimelineModel.Filter} |
| */ |
| WebInspector.TimelineCategoryFilter = function() |
| { |
| WebInspector.TimelineModel.Filter.call(this); |
| } |
| |
| WebInspector.TimelineCategoryFilter.prototype = { |
| /** |
| * @override |
| * @param {!WebInspector.TracingModel.Event} event |
| * @return {boolean} |
| */ |
| accept: function(event) |
| { |
| return !WebInspector.TimelineUIUtils.eventStyle(event).category.hidden; |
| }, |
| |
| __proto__: WebInspector.TimelineModel.Filter.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.TimelineModel.Filter} |
| */ |
| WebInspector.TimelineIsLongFilter = function() |
| { |
| WebInspector.TimelineModel.Filter.call(this); |
| this._minimumRecordDuration = 0; |
| } |
| |
| WebInspector.TimelineIsLongFilter.prototype = { |
| /** |
| * @param {number} value |
| */ |
| setMinimumRecordDuration: function(value) |
| { |
| this._minimumRecordDuration = value; |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.TracingModel.Event} event |
| * @return {boolean} |
| */ |
| accept: function(event) |
| { |
| var duration = event.endTime ? event.endTime - event.startTime : 0; |
| return duration >= this._minimumRecordDuration; |
| }, |
| |
| __proto__: WebInspector.TimelineModel.Filter.prototype |
| |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.TimelineModel.Filter} |
| * @param {!RegExp=} regExp |
| */ |
| WebInspector.TimelineTextFilter = function(regExp) |
| { |
| WebInspector.TimelineModel.Filter.call(this); |
| this._setRegExp(regExp || null); |
| } |
| |
| WebInspector.TimelineTextFilter.prototype = { |
| /** |
| * @param {?RegExp} regExp |
| */ |
| _setRegExp: function(regExp) |
| { |
| this._regExp = regExp; |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.TracingModel.Event} event |
| * @return {boolean} |
| */ |
| accept: function(event) |
| { |
| return !this._regExp || WebInspector.TimelineUIUtils.testContentMatching(event, this._regExp); |
| }, |
| |
| __proto__: WebInspector.TimelineModel.Filter.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.VBox} |
| * @param {boolean} showTimer |
| * @param {function()} stopCallback |
| */ |
| WebInspector.TimelinePanel.StatusPane = function(showTimer, stopCallback) |
| { |
| WebInspector.VBox.call(this, true); |
| this.registerRequiredCSS("timeline/timelineStatusDialog.css"); |
| this.contentElement.classList.add("timeline-status-dialog"); |
| |
| var statusLine = this.contentElement.createChild("div", "status-dialog-line status"); |
| statusLine.createChild("div", "label").textContent = WebInspector.UIString("Status"); |
| this._status = statusLine.createChild("div", "content"); |
| |
| if (showTimer) { |
| var timeLine = this.contentElement.createChild("div", "status-dialog-line time"); |
| timeLine.createChild("div", "label").textContent = WebInspector.UIString("Time"); |
| this._time = timeLine.createChild("div", "content"); |
| } |
| var progressLine = this.contentElement.createChild("div", "status-dialog-line progress"); |
| this._progressLabel = progressLine.createChild("div", "label"); |
| this._progressBar = progressLine.createChild("div", "indicator-container").createChild("div", "indicator"); |
| |
| this._stopButton = createTextButton(WebInspector.UIString("Stop"), stopCallback); |
| this.contentElement.createChild("div", "stop-button").appendChild(this._stopButton); |
| } |
| |
| WebInspector.TimelinePanel.StatusPane.prototype = { |
| finish: function() |
| { |
| this._stopTimer(); |
| this._stopButton.disabled = true; |
| }, |
| |
| hide: function() |
| { |
| this.element.parentNode.classList.remove("tinted"); |
| this.element.remove(); |
| }, |
| |
| /** |
| * @param {!Element} parent |
| */ |
| showPane: function(parent) |
| { |
| this.show(parent); |
| parent.classList.add("tinted"); |
| }, |
| |
| /** |
| * @param {string} text |
| */ |
| updateStatus: function(text) |
| { |
| this._status.textContent = text; |
| }, |
| |
| /** |
| * @param {string} activity |
| * @param {number} percent |
| */ |
| updateProgressBar: function(activity, percent) |
| { |
| this._progressLabel.textContent = activity; |
| this._progressBar.style.width = percent.toFixed(1) + "%"; |
| this._updateTimer(); |
| }, |
| |
| startTimer: function() |
| { |
| this._startTime = Date.now(); |
| this._timeUpdateTimer = setInterval(this._updateTimer.bind(this, false), 1000); |
| this._updateTimer(); |
| }, |
| |
| _stopTimer: function() |
| { |
| if (!this._timeUpdateTimer) |
| return; |
| clearInterval(this._timeUpdateTimer); |
| this._updateTimer(true); |
| delete this._timeUpdateTimer; |
| }, |
| |
| /** |
| * @param {boolean=} precise |
| */ |
| _updateTimer: function(precise) |
| { |
| if (!this._timeUpdateTimer) |
| return; |
| var elapsed = (Date.now() - this._startTime) / 1000; |
| this._time.textContent = WebInspector.UIString("%s\u2009sec", elapsed.toFixed(precise ? 1 : 0)); |
| }, |
| |
| __proto__: WebInspector.VBox.prototype |
| } |
| |
| WebInspector.TimelinePanel.show = function() |
| { |
| WebInspector.inspectorView.setCurrentPanel(WebInspector.TimelinePanel.instance()); |
| } |
| |
| /** |
| * @return {!WebInspector.TimelinePanel} |
| */ |
| WebInspector.TimelinePanel.instance = function() |
| { |
| return /** @type {!WebInspector.TimelinePanel} */ (self.runtime.sharedInstance(WebInspector.TimelinePanel)); |
| } |
| |
| /** |
| * @constructor |
| * @implements {WebInspector.QueryParamHandler} |
| */ |
| WebInspector.LoadTimelineHandler = function() |
| { |
| } |
| |
| WebInspector.LoadTimelineHandler.prototype = { |
| /** |
| * @override |
| * @param {string} value |
| */ |
| handleQueryParam: function(value) |
| { |
| WebInspector.TimelinePanel.show(); |
| WebInspector.TimelinePanel.instance()._loadFromURL(window.decodeURIComponent(value)); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @implements {WebInspector.ActionDelegate} |
| */ |
| WebInspector.TimelinePanel.ActionDelegate = function() |
| { |
| } |
| |
| WebInspector.TimelinePanel.ActionDelegate.prototype = { |
| /** |
| * @override |
| * @param {!WebInspector.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction: function(context, actionId) |
| { |
| var panel = WebInspector.context.flavor(WebInspector.TimelinePanel); |
| console.assert(panel && panel instanceof WebInspector.TimelinePanel); |
| switch (actionId) { |
| case "timeline.toggle-recording": |
| panel._toggleRecording(); |
| return true; |
| case "timeline.save-to-file": |
| panel._saveToFile(); |
| return true; |
| case "timeline.load-from-file": |
| panel._selectFileToLoad(); |
| return true; |
| case "timeline.jump-to-previous-frame": |
| panel._jumpToFrame(-1); |
| return true; |
| case "timeline.jump-to-next-frame": |
| panel._jumpToFrame(1); |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| */ |
| WebInspector.TimelineFilters = function() |
| { |
| WebInspector.Object.call(this); |
| |
| this._categoryFilter = new WebInspector.TimelineCategoryFilter(); |
| this._durationFilter = new WebInspector.TimelineIsLongFilter(); |
| this._textFilter = new WebInspector.TimelineTextFilter(); |
| this._filters = [this._categoryFilter, this._durationFilter, this._textFilter]; |
| |
| this._createFilterBar(); |
| } |
| |
| WebInspector.TimelineFilters.Events = { |
| FilterChanged: Symbol("FilterChanged") |
| }; |
| |
| WebInspector.TimelineFilters._durationFilterPresetsMs = [0, 1, 15]; |
| |
| WebInspector.TimelineFilters.prototype = { |
| /** |
| * @return {!Array<!WebInspector.TimelineModel.Filter>} |
| */ |
| filters: function() |
| { |
| return this._filters; |
| }, |
| |
| /** |
| * @return {?RegExp} |
| */ |
| searchRegExp: function() |
| { |
| return this._textFilter._regExp; |
| }, |
| |
| /** |
| * @return {!WebInspector.ToolbarItem} |
| */ |
| filterButton: function() |
| { |
| return this._filterBar.filterButton(); |
| }, |
| |
| /** |
| * @return {!WebInspector.Widget} |
| */ |
| filtersWidget: function() |
| { |
| return this._filterBar; |
| }, |
| |
| _createFilterBar: function() |
| { |
| this._filterBar = new WebInspector.FilterBar("timelinePanel"); |
| |
| this._textFilterUI = new WebInspector.TextFilterUI(); |
| this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, textFilterChanged, this); |
| this._filterBar.addFilter(this._textFilterUI); |
| |
| var durationOptions = []; |
| for (var durationMs of WebInspector.TimelineFilters._durationFilterPresetsMs) { |
| var durationOption = {}; |
| if (!durationMs) { |
| durationOption.label = WebInspector.UIString("All"); |
| durationOption.title = WebInspector.UIString("Show all records"); |
| } else { |
| durationOption.label = WebInspector.UIString("\u2265 %dms", durationMs); |
| durationOption.title = WebInspector.UIString("Hide records shorter than %dms", durationMs); |
| } |
| durationOption.value = durationMs; |
| durationOptions.push(durationOption); |
| } |
| var durationFilterUI = new WebInspector.ComboBoxFilterUI(durationOptions); |
| durationFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, durationFilterChanged, this); |
| this._filterBar.addFilter(durationFilterUI); |
| |
| var categoryFiltersUI = {}; |
| var categories = WebInspector.TimelineUIUtils.categories(); |
| for (var categoryName in categories) { |
| var category = categories[categoryName]; |
| if (!category.visible) |
| continue; |
| var filter = new WebInspector.CheckboxFilterUI(category.name, category.title); |
| filter.setColor(category.color, "rgba(0, 0, 0, 0.2)"); |
| categoryFiltersUI[category.name] = filter; |
| filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, categoriesFilterChanged.bind(this, categoryName)); |
| this._filterBar.addFilter(filter); |
| } |
| return this._filterBar; |
| |
| /** |
| * @this {WebInspector.TimelineFilters} |
| */ |
| function textFilterChanged() |
| { |
| var searchQuery = this._textFilterUI.value(); |
| this._textFilter._setRegExp(searchQuery ? createPlainTextSearchRegex(searchQuery, "i") : null); |
| this._notifyFiltersChanged(); |
| } |
| |
| /** |
| * @this {WebInspector.TimelineFilters} |
| */ |
| function durationFilterChanged() |
| { |
| var duration = durationFilterUI.value(); |
| var minimumRecordDuration = parseInt(duration, 10); |
| this._durationFilter.setMinimumRecordDuration(minimumRecordDuration); |
| this._notifyFiltersChanged(); |
| } |
| |
| /** |
| * @param {string} name |
| * @this {WebInspector.TimelineFilters} |
| */ |
| function categoriesFilterChanged(name) |
| { |
| var categories = WebInspector.TimelineUIUtils.categories(); |
| categories[name].hidden = !categoryFiltersUI[name].checked(); |
| this._notifyFiltersChanged(); |
| } |
| }, |
| |
| _notifyFiltersChanged: function() |
| { |
| this.dispatchEventToListeners(WebInspector.TimelineFilters.Events.FilterChanged); |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| }; |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| * @implements {WebInspector.TargetManager.Observer} |
| */ |
| WebInspector.CPUThrottlingManager = function() |
| { |
| this._targets = []; |
| this._throttlingRate = 1.; // No throttling |
| WebInspector.targetManager.observeTargets(this, WebInspector.Target.Capability.Browser); |
| } |
| |
| WebInspector.CPUThrottlingManager.prototype = { |
| /** |
| * @param {number} value |
| */ |
| setRate: function(value) |
| { |
| this._throttlingRate = value; |
| this._targets.forEach(target => target.emulationAgent().setCPUThrottlingRate(value)); |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| rate: function() |
| { |
| return this._throttlingRate; |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.Target} target |
| */ |
| targetAdded: function(target) |
| { |
| this._targets.push(target); |
| target.emulationAgent().setCPUThrottlingRate(this._throttlingRate); |
| }, |
| |
| /** |
| * @override |
| * @param {!WebInspector.Target} target |
| */ |
| targetRemoved: function(target) |
| { |
| this._targets.remove(target, true); |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HBox} |
| */ |
| WebInspector.TimelinePanel.CustomCPUThrottlingRateDialog = function() |
| { |
| WebInspector.HBox.call(this, true); |
| this.registerRequiredCSS("ui_lazy/dialog.css"); |
| this.contentElement.createChild("label").textContent = WebInspector.UIString("CPU Slowdown Rate: "); |
| |
| this._input = this.contentElement.createChild("input"); |
| this._input.setAttribute("type", "text"); |
| this._input.style.width = "64px"; |
| this._input.addEventListener("keydown", this._onKeyDown.bind(this), false); |
| |
| var addButton = this.contentElement.createChild("button"); |
| addButton.textContent = WebInspector.UIString("Set"); |
| addButton.addEventListener("click", this._apply.bind(this), false); |
| |
| this.setDefaultFocusedElement(this._input); |
| this.contentElement.tabIndex = 0; |
| this._resultPromise = new Promise(fulfill => this._callback = fulfill); |
| } |
| |
| /** |
| * @param {!Element=} anchor |
| * @return {!Promise<string>} |
| */ |
| WebInspector.TimelinePanel.CustomCPUThrottlingRateDialog.show = function(anchor) |
| { |
| var dialog = new WebInspector.Dialog(); |
| var dialogContent = new WebInspector.TimelinePanel.CustomCPUThrottlingRateDialog(); |
| dialogContent.show(dialog.element); |
| dialog.setWrapsContent(true); |
| if (anchor) |
| dialog.setPosition(anchor.totalOffsetLeft() - 32, anchor.totalOffsetTop() + anchor.offsetHeight); |
| dialog.show(); |
| return dialogContent.result().then(value => (dialog.detach(), value)); |
| } |
| |
| WebInspector.TimelinePanel.CustomCPUThrottlingRateDialog.prototype = { |
| /** |
| * @return {!Promise<string>} |
| */ |
| result: function() |
| { |
| return this._resultPromise; |
| }, |
| |
| _apply: function() |
| { |
| this._callback(this._input.value); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _onKeyDown: function(event) |
| { |
| if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code) { |
| event.preventDefault(); |
| this._apply(); |
| } |
| }, |
| |
| __proto__: WebInspector.HBox.prototype |
| } |