| /* | |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
| * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> | |
| * Copyright (C) 2011 Google 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: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. 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. | |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. | |
| */ | |
| /** | |
| * @implements {UI.ContextMenu.Provider} | |
| * @implements {UI.ViewLocationResolver} | |
| */ | |
| export default class NetworkPanel extends UI.Panel { | |
| constructor() { | |
| super('network'); | |
| this.registerRequiredCSS('network/networkPanel.css'); | |
| this._networkLogShowOverviewSetting = Common.settings.createSetting('networkLogShowOverview', true); | |
| this._networkLogLargeRowsSetting = Common.settings.createSetting('networkLogLargeRows', false); | |
| this._networkRecordFilmStripSetting = Common.settings.createSetting('networkRecordFilmStripSetting', false); | |
| this._toggleRecordAction = /** @type {!UI.Action }*/ (UI.actionRegistry.action('network.toggle-recording')); | |
| /** @type {number|undefined} */ | |
| this._pendingStopTimer; | |
| /** @type {?Network.NetworkItemView} */ | |
| this._networkItemView = null; | |
| /** @type {?PerfUI.FilmStripView} */ | |
| this._filmStripView = null; | |
| /** @type {?FilmStripRecorder} */ | |
| this._filmStripRecorder = null; | |
| /** @type {?SDK.NetworkRequest} */ | |
| this._currentRequest = null; | |
| const panel = new UI.VBox(); | |
| const networkToolbarContainer = panel.contentElement.createChild('div', 'network-toolbar-container'); | |
| this._panelToolbar = new UI.Toolbar('', networkToolbarContainer); | |
| this._rightToolbar = new UI.Toolbar('', networkToolbarContainer); | |
| this._filterBar = new UI.FilterBar('networkPanel', true); | |
| this._filterBar.show(panel.contentElement); | |
| this._settingsPane = new UI.HBox(); | |
| this._settingsPane.element.classList.add('network-settings-pane'); | |
| this._settingsPane.show(panel.contentElement); | |
| this._showSettingsPaneSetting = Common.settings.createSetting('networkShowSettingsToolbar', false); | |
| this._showSettingsPaneSetting.addChangeListener(this._updateSettingsPaneVisibility.bind(this)); | |
| this._updateSettingsPaneVisibility(); | |
| this._filmStripPlaceholderElement = panel.contentElement.createChild('div', 'network-film-strip-placeholder'); | |
| // Create top overview component. | |
| this._overviewPane = new PerfUI.TimelineOverviewPane('network'); | |
| this._overviewPane.addEventListener( | |
| PerfUI.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this)); | |
| this._overviewPane.element.id = 'network-overview-panel'; | |
| this._networkOverview = new Network.NetworkOverview(); | |
| this._overviewPane.setOverviewControls([this._networkOverview]); | |
| this._overviewPlaceholderElement = panel.contentElement.createChild('div'); | |
| this._calculator = new Network.NetworkTransferTimeCalculator(); | |
| this._splitWidget = new UI.SplitWidget(true, false, 'networkPanelSplitViewState'); | |
| this._splitWidget.hideMain(); | |
| this._splitWidget.show(panel.contentElement); | |
| panel.setDefaultFocusedChild(this._filterBar); | |
| const initialSidebarWidth = 225; | |
| const splitWidget = new UI.SplitWidget(true, false, 'networkPanelSidebarState', initialSidebarWidth); | |
| splitWidget.hideSidebar(); | |
| splitWidget.enableShowModeSaving(); | |
| splitWidget.show(this.element); | |
| this._sidebarLocation = UI.viewManager.createTabbedLocation(async () => { | |
| UI.viewManager.showView('network'); | |
| splitWidget.showBoth(); | |
| }, 'network-sidebar', true); | |
| const tabbedPane = this._sidebarLocation.tabbedPane(); | |
| tabbedPane.setMinimumSize(100, 25); | |
| tabbedPane.element.classList.add('network-tabbed-pane'); | |
| tabbedPane.element.addEventListener('keydown', event => { | |
| if (event.key !== 'Escape') { | |
| return; | |
| } | |
| splitWidget.hideSidebar(); | |
| event.consume(); | |
| }); | |
| const closeSidebar = new UI.ToolbarButton(Common.UIString('Close'), 'largeicon-delete'); | |
| closeSidebar.addEventListener(UI.ToolbarButton.Events.Click, () => splitWidget.hideSidebar()); | |
| tabbedPane.rightToolbar().appendToolbarItem(closeSidebar); | |
| splitWidget.setSidebarWidget(tabbedPane); | |
| splitWidget.setMainWidget(panel); | |
| splitWidget.setDefaultFocusedChild(panel); | |
| this.setDefaultFocusedChild(splitWidget); | |
| this._progressBarContainer = createElement('div'); | |
| /** @type {!Network.NetworkLogView} */ | |
| this._networkLogView = | |
| new Network.NetworkLogView(this._filterBar, this._progressBarContainer, this._networkLogLargeRowsSetting); | |
| this._splitWidget.setSidebarWidget(this._networkLogView); | |
| this._fileSelectorElement = | |
| UI.createFileSelectorElement(this._networkLogView.onLoadFromFile.bind(this._networkLogView)); | |
| panel.element.appendChild(this._fileSelectorElement); | |
| this._detailsWidget = new UI.VBox(); | |
| this._detailsWidget.element.classList.add('network-details-view'); | |
| this._splitWidget.setMainWidget(this._detailsWidget); | |
| this._closeButtonElement = createElement('div', 'dt-close-button'); | |
| this._closeButtonElement.addEventListener( | |
| 'click', async () => await UI.actionRegistry.action('network.hide-request-details').execute(), false); | |
| this._closeButtonElement.style.margin = '0 5px'; | |
| this._networkLogShowOverviewSetting.addChangeListener(this._toggleShowOverview, this); | |
| this._networkLogLargeRowsSetting.addChangeListener(this._toggleLargerRequests, this); | |
| this._networkRecordFilmStripSetting.addChangeListener(this._toggleRecordFilmStrip, this); | |
| this._preserveLogSetting = Common.moduleSetting('network_log.preserve-log'); | |
| this._throttlingSelect = this._createThrottlingConditionsSelect(); | |
| this._setupToolbarButtons(splitWidget); | |
| this._toggleRecord(true); | |
| this._toggleShowOverview(); | |
| this._toggleLargerRequests(); | |
| this._toggleRecordFilmStrip(); | |
| this._updateUI(); | |
| SDK.targetManager.addModelListener( | |
| SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.WillReloadPage, this._willReloadPage, this); | |
| SDK.targetManager.addModelListener(SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this._load, this); | |
| this._networkLogView.addEventListener(Network.NetworkLogView.Events.RequestSelected, this._onRequestSelected, this); | |
| this._networkLogView.addEventListener( | |
| Network.NetworkLogView.Events.RequestActivated, this._onRequestActivated, this); | |
| SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestAdded, this._onUpdateRequest, this); | |
| SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestUpdated, this._onUpdateRequest, this); | |
| SDK.networkLog.addEventListener(SDK.NetworkLog.Events.Reset, this._onNetworkLogReset, this); | |
| } | |
| /** | |
| * @param {!Array<{filterType: !Network.NetworkLogView.FilterType, filterValue: string}>} filters | |
| */ | |
| static revealAndFilter(filters) { | |
| const panel = NetworkPanel._instance(); | |
| let filterString = ''; | |
| for (const filter of filters) { | |
| filterString += `${filter.filterType}:${filter.filterValue} `; | |
| } | |
| panel._networkLogView.setTextFilterValue(filterString); | |
| UI.viewManager.showView('network'); | |
| } | |
| /** | |
| * @return {!NetworkPanel} | |
| */ | |
| static _instance() { | |
| return /** @type {!NetworkPanel} */ (self.runtime.sharedInstance(NetworkPanel)); | |
| } | |
| /** | |
| * @return {!UI.ToolbarComboBox} | |
| */ | |
| throttlingSelectForTest() { | |
| return this._throttlingSelect; | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onWindowChanged(event) { | |
| const startTime = Math.max(this._calculator.minimumBoundary(), event.data.startTime / 1000); | |
| const endTime = Math.min(this._calculator.maximumBoundary(), event.data.endTime / 1000); | |
| this._networkLogView.setWindow(startTime, endTime); | |
| } | |
| _setupToolbarButtons(splitWidget) { | |
| const searchToggle = new UI.ToolbarToggle(ls`Search`, 'largeicon-search'); | |
| function updateSidebarToggle() { | |
| const isSidebarShowing = splitWidget.showMode() !== UI.SplitWidget.ShowMode.OnlyMain; | |
| searchToggle.setToggled(isSidebarShowing); | |
| if (!isSidebarShowing) { | |
| searchToggle.element.focus(); | |
| } | |
| } | |
| this._panelToolbar.appendToolbarItem(UI.Toolbar.createActionButton(this._toggleRecordAction)); | |
| const clearButton = new UI.ToolbarButton(Common.UIString('Clear'), 'largeicon-clear'); | |
| clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => SDK.networkLog.reset(), this); | |
| this._panelToolbar.appendToolbarItem(clearButton); | |
| this._panelToolbar.appendSeparator(); | |
| this._panelToolbar.appendToolbarItem(this._filterBar.filterButton()); | |
| updateSidebarToggle(); | |
| splitWidget.addEventListener(UI.SplitWidget.Events.ShowModeChanged, updateSidebarToggle); | |
| searchToggle.addEventListener(UI.ToolbarButton.Events.Click, () => { | |
| if (splitWidget.showMode() === UI.SplitWidget.ShowMode.OnlyMain) { | |
| splitWidget.showBoth(); | |
| } else { | |
| splitWidget.hideSidebar(); | |
| } | |
| }); | |
| this._panelToolbar.appendToolbarItem(searchToggle); | |
| this._panelToolbar.appendSeparator(); | |
| this._panelToolbar.appendToolbarItem(new UI.ToolbarSettingCheckbox( | |
| this._preserveLogSetting, Common.UIString('Do not clear log on page reload / navigation'), | |
| Common.UIString('Preserve log'))); | |
| const disableCacheCheckbox = new UI.ToolbarSettingCheckbox( | |
| Common.moduleSetting('cacheDisabled'), Common.UIString('Disable cache (while DevTools is open)'), | |
| Common.UIString('Disable cache')); | |
| this._panelToolbar.appendToolbarItem(disableCacheCheckbox); | |
| this._panelToolbar.appendSeparator(); | |
| this._panelToolbar.appendToolbarItem(this._throttlingSelect); | |
| this._rightToolbar.appendToolbarItem(new UI.ToolbarItem(this._progressBarContainer)); | |
| this._rightToolbar.appendSeparator(); | |
| this._rightToolbar.appendToolbarItem( | |
| new UI.ToolbarSettingToggle(this._showSettingsPaneSetting, 'largeicon-settings-gear', ls`Network settings`)); | |
| const settingsToolbarLeft = new UI.Toolbar('', this._settingsPane.element); | |
| settingsToolbarLeft.makeVertical(); | |
| settingsToolbarLeft.appendToolbarItem( | |
| new UI.ToolbarSettingCheckbox(this._networkLogLargeRowsSetting, '', ls`Use large request rows`)); | |
| settingsToolbarLeft.appendToolbarItem( | |
| new UI.ToolbarSettingCheckbox(this._networkLogShowOverviewSetting, '', ls`Show overview`)); | |
| const settingsToolbarRight = new UI.Toolbar('', this._settingsPane.element); | |
| settingsToolbarRight.makeVertical(); | |
| settingsToolbarRight.appendToolbarItem( | |
| new UI.ToolbarSettingCheckbox(Common.moduleSetting('network.group-by-frame'), '', ls`Group by frame`)); | |
| settingsToolbarRight.appendToolbarItem( | |
| new UI.ToolbarSettingCheckbox(this._networkRecordFilmStripSetting, '', ls`Capture screenshots`)); | |
| this._panelToolbar.appendSeparator(); | |
| const importHarButton = new UI.ToolbarButton(ls`Import HAR file...`, 'largeicon-load'); | |
| importHarButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._fileSelectorElement.click(), this); | |
| this._panelToolbar.appendToolbarItem(importHarButton); | |
| const exportHarButton = new UI.ToolbarButton(ls`Export HAR...`, 'largeicon-download'); | |
| exportHarButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._networkLogView.exportAll(), this); | |
| this._panelToolbar.appendToolbarItem(exportHarButton); | |
| } | |
| _updateSettingsPaneVisibility() { | |
| this._settingsPane.element.classList.toggle('hidden', !this._showSettingsPaneSetting.get()); | |
| } | |
| /** | |
| * @return {!UI.ToolbarComboBox} | |
| */ | |
| _createThrottlingConditionsSelect() { | |
| const toolbarItem = new UI.ToolbarComboBox(null, ls`Throttling`); | |
| toolbarItem.setMaxWidth(160); | |
| MobileThrottling.throttlingManager().decorateSelectWithNetworkThrottling(toolbarItem.selectElement()); | |
| return toolbarItem; | |
| } | |
| _toggleRecording() { | |
| if (!this._preserveLogSetting.get() && !this._toggleRecordAction.toggled()) { | |
| SDK.networkLog.reset(); | |
| } | |
| this._toggleRecord(!this._toggleRecordAction.toggled()); | |
| } | |
| /** | |
| * @param {boolean} toggled | |
| */ | |
| _toggleRecord(toggled) { | |
| this._toggleRecordAction.setToggled(toggled); | |
| this._networkLogView.setRecording(toggled); | |
| if (!toggled && this._filmStripRecorder) { | |
| this._filmStripRecorder.stopRecording(this._filmStripAvailable.bind(this)); | |
| } | |
| // TODO(einbinder) This should be moved to a setting/action that NetworkLog owns but NetworkPanel controls, but | |
| // always be present in the command menu. | |
| SDK.networkLog.setIsRecording(toggled); | |
| } | |
| /** | |
| * @param {?SDK.FilmStripModel} filmStripModel | |
| */ | |
| _filmStripAvailable(filmStripModel) { | |
| if (!filmStripModel) { | |
| return; | |
| } | |
| const calculator = this._networkLogView.timeCalculator(); | |
| this._filmStripView.setModel(filmStripModel, calculator.minimumBoundary() * 1000, calculator.boundarySpan() * 1000); | |
| this._networkOverview.setFilmStripModel(filmStripModel); | |
| const timestamps = filmStripModel.frames().map(mapTimestamp); | |
| /** | |
| * @param {!SDK.FilmStripModel.Frame} frame | |
| * @return {number} | |
| */ | |
| function mapTimestamp(frame) { | |
| return frame.timestamp / 1000; | |
| } | |
| this._networkLogView.addFilmStripFrames(timestamps); | |
| } | |
| _onNetworkLogReset() { | |
| Network.BlockedURLsPane.reset(); | |
| if (!this._preserveLogSetting.get()) { | |
| this._calculator.reset(); | |
| this._overviewPane.reset(); | |
| } | |
| if (this._filmStripView) { | |
| this._resetFilmStripView(); | |
| } | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _willReloadPage(event) { | |
| this._toggleRecord(true); | |
| if (this._pendingStopTimer) { | |
| clearTimeout(this._pendingStopTimer); | |
| delete this._pendingStopTimer; | |
| } | |
| if (this.isShowing() && this._filmStripRecorder) { | |
| this._filmStripRecorder.startRecording(); | |
| } | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _load(event) { | |
| if (this._filmStripRecorder && this._filmStripRecorder.isRecording()) { | |
| this._pendingStopTimer = setTimeout(this._stopFilmStripRecording.bind(this), displayScreenshotDelay); | |
| } | |
| } | |
| _stopFilmStripRecording() { | |
| this._filmStripRecorder.stopRecording(this._filmStripAvailable.bind(this)); | |
| delete this._pendingStopTimer; | |
| } | |
| _toggleLargerRequests() { | |
| this._updateUI(); | |
| } | |
| _toggleShowOverview() { | |
| const toggled = this._networkLogShowOverviewSetting.get(); | |
| if (toggled) { | |
| this._overviewPane.show(this._overviewPlaceholderElement); | |
| } else { | |
| this._overviewPane.detach(); | |
| } | |
| this.doResize(); | |
| } | |
| _toggleRecordFilmStrip() { | |
| const toggled = this._networkRecordFilmStripSetting.get(); | |
| if (toggled && !this._filmStripRecorder) { | |
| this._filmStripView = new PerfUI.FilmStripView(); | |
| this._filmStripView.setMode(PerfUI.FilmStripView.Modes.FrameBased); | |
| this._filmStripView.element.classList.add('network-film-strip'); | |
| this._filmStripRecorder = new FilmStripRecorder(this._networkLogView.timeCalculator(), this._filmStripView); | |
| this._filmStripView.show(this._filmStripPlaceholderElement); | |
| this._filmStripView.addEventListener(PerfUI.FilmStripView.Events.FrameSelected, this._onFilmFrameSelected, this); | |
| this._filmStripView.addEventListener(PerfUI.FilmStripView.Events.FrameEnter, this._onFilmFrameEnter, this); | |
| this._filmStripView.addEventListener(PerfUI.FilmStripView.Events.FrameExit, this._onFilmFrameExit, this); | |
| this._resetFilmStripView(); | |
| } | |
| if (!toggled && this._filmStripRecorder) { | |
| this._filmStripView.detach(); | |
| this._filmStripView = null; | |
| this._filmStripRecorder = null; | |
| } | |
| } | |
| _resetFilmStripView() { | |
| const reloadShortcutDescriptor = UI.shortcutRegistry.shortcutDescriptorsForAction('inspector_main.reload')[0]; | |
| this._filmStripView.reset(); | |
| if (reloadShortcutDescriptor) { | |
| this._filmStripView.setStatusText( | |
| Common.UIString('Hit %s to reload and capture filmstrip.', reloadShortcutDescriptor.name)); | |
| } | |
| } | |
| /** | |
| * @override | |
| * @return {!Array.<!Element>} | |
| */ | |
| elementsToRestoreScrollPositionsFor() { | |
| return this._networkLogView.elementsToRestoreScrollPositionsFor(); | |
| } | |
| /** | |
| * @override | |
| */ | |
| wasShown() { | |
| UI.context.setFlavor(NetworkPanel, this); | |
| // Record the network tool load time after the panel has loaded. | |
| Host.userMetrics.panelLoaded('network', 'DevTools.Launch.Network'); | |
| } | |
| /** | |
| * @override | |
| */ | |
| willHide() { | |
| UI.context.setFlavor(NetworkPanel, null); | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| */ | |
| revealAndHighlightRequest(request) { | |
| this._hideRequestPanel(); | |
| if (request) { | |
| this._networkLogView.revealAndHighlightRequest(request); | |
| } | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @return {!Promise<?Network.NetworkItemView>} | |
| */ | |
| async selectRequest(request) { | |
| await UI.viewManager.showView('network'); | |
| this._networkLogView.selectRequest(request); | |
| return this._networkItemView; | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onRowSizeChanged(event) { | |
| this._updateUI(); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onRequestSelected(event) { | |
| const request = /** @type {?SDK.NetworkRequest} */ (event.data); | |
| this._currentRequest = request; | |
| this._networkOverview.setHighlightedRequest(request); | |
| this._updateNetworkItemView(); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onRequestActivated(event) { | |
| const showPanel = /** @type {boolean} */ (event.data); | |
| if (showPanel) { | |
| this._showRequestPanel(); | |
| } else { | |
| this._hideRequestPanel(); | |
| } | |
| } | |
| _showRequestPanel() { | |
| this._clearNetworkItemView(); | |
| if (this._currentRequest) { | |
| this._createNetworkItemView(); | |
| } | |
| this._updateUI(); | |
| } | |
| _hideRequestPanel() { | |
| this._clearNetworkItemView(); | |
| this._splitWidget.hideMain(); | |
| this._updateUI(); | |
| } | |
| _updateNetworkItemView() { | |
| if (this._splitWidget.showMode() === UI.SplitWidget.ShowMode.Both) { | |
| this._clearNetworkItemView(); | |
| this._createNetworkItemView(); | |
| this._updateUI(); | |
| } | |
| } | |
| _clearNetworkItemView() { | |
| if (this._networkItemView) { | |
| this._networkItemView.detach(); | |
| this._networkItemView = null; | |
| } | |
| } | |
| _createNetworkItemView() { | |
| if (!this._currentRequest) { | |
| return; | |
| } | |
| this._networkItemView = new Network.NetworkItemView(this._currentRequest, this._networkLogView.timeCalculator()); | |
| this._networkItemView.leftToolbar().appendToolbarItem(new UI.ToolbarItem(this._closeButtonElement)); | |
| this._networkItemView.show(this._detailsWidget.element); | |
| this._splitWidget.showBoth(); | |
| } | |
| _updateUI() { | |
| this._detailsWidget.element.classList.toggle( | |
| 'network-details-view-tall-header', this._networkLogLargeRowsSetting.get()); | |
| this._networkLogView.switchViewMode(!this._splitWidget.isResizable()); | |
| } | |
| /** | |
| * @override | |
| * @param {!Event} event | |
| * @param {!UI.ContextMenu} contextMenu | |
| * @param {!Object} target | |
| * @this {NetworkPanel} | |
| */ | |
| appendApplicableItems(event, contextMenu, target) { | |
| /** | |
| * @this {NetworkPanel} | |
| */ | |
| function reveal(request) { | |
| UI.viewManager.showView('network').then(this.revealAndHighlightRequest.bind(this, request)); | |
| } | |
| /** | |
| * @this {NetworkPanel} | |
| */ | |
| function appendRevealItem(request) { | |
| contextMenu.revealSection().appendItem(Common.UIString('Reveal in Network panel'), reveal.bind(this, request)); | |
| } | |
| if (event.target.isSelfOrDescendant(this.element)) { | |
| return; | |
| } | |
| if (target instanceof SDK.Resource) { | |
| const resource = /** @type {!SDK.Resource} */ (target); | |
| if (resource.request) { | |
| appendRevealItem.call(this, resource.request); | |
| } | |
| return; | |
| } | |
| if (target instanceof Workspace.UISourceCode) { | |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (target); | |
| const resource = Bindings.resourceForURL(uiSourceCode.url()); | |
| if (resource && resource.request) { | |
| appendRevealItem.call(this, resource.request); | |
| } | |
| return; | |
| } | |
| if (!(target instanceof SDK.NetworkRequest)) { | |
| return; | |
| } | |
| const request = /** @type {!SDK.NetworkRequest} */ (target); | |
| if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request) { | |
| return; | |
| } | |
| appendRevealItem.call(this, request); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onFilmFrameSelected(event) { | |
| const timestamp = /** @type {number} */ (event.data); | |
| this._overviewPane.setWindowTimes(0, timestamp); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onFilmFrameEnter(event) { | |
| const timestamp = /** @type {number} */ (event.data); | |
| this._networkOverview.selectFilmStripFrame(timestamp); | |
| this._networkLogView.selectFilmStripFrame(timestamp / 1000); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onFilmFrameExit(event) { | |
| this._networkOverview.clearFilmStripFrame(); | |
| this._networkLogView.clearFilmStripFrame(); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _onUpdateRequest(event) { | |
| const request = /** @type {!SDK.NetworkRequest} */ (event.data); | |
| this._calculator.updateBoundaries(request); | |
| // FIXME: Unify all time units across the frontend! | |
| this._overviewPane.setBounds(this._calculator.minimumBoundary() * 1000, this._calculator.maximumBoundary() * 1000); | |
| this._networkOverview.updateRequest(request); | |
| this._overviewPane.scheduleUpdate(); | |
| } | |
| /** | |
| * @override | |
| * @param {string} locationName | |
| * @return {?UI.ViewLocation} | |
| */ | |
| resolveLocation(locationName) { | |
| if (locationName === 'network-sidebar') { | |
| return this._sidebarLocation; | |
| } | |
| return null; | |
| } | |
| } | |
| export const displayScreenshotDelay = 1000; | |
| /** | |
| * @implements {UI.ContextMenu.Provider} | |
| * @unrestricted | |
| */ | |
| export class ContextMenuProvider { | |
| /** | |
| * @override | |
| * @param {!Event} event | |
| * @param {!UI.ContextMenu} contextMenu | |
| * @param {!Object} target | |
| */ | |
| appendApplicableItems(event, contextMenu, target) { | |
| NetworkPanel._instance().appendApplicableItems(event, contextMenu, target); | |
| } | |
| } | |
| /** | |
| * @implements {Common.Revealer} | |
| * @unrestricted | |
| */ | |
| export class RequestRevealer { | |
| /** | |
| * @override | |
| * @param {!Object} request | |
| * @return {!Promise} | |
| */ | |
| reveal(request) { | |
| if (!(request instanceof SDK.NetworkRequest)) { | |
| return Promise.reject(new Error('Internal error: not a network request')); | |
| } | |
| const panel = NetworkPanel._instance(); | |
| return UI.viewManager.showView('network').then(panel.revealAndHighlightRequest.bind(panel, request)); | |
| } | |
| } | |
| /** | |
| * @implements {SDK.TracingManagerClient} | |
| */ | |
| export class FilmStripRecorder { | |
| /** | |
| * @param {!Network.NetworkTimeCalculator} timeCalculator | |
| * @param {!PerfUI.FilmStripView} filmStripView | |
| */ | |
| constructor(timeCalculator, filmStripView) { | |
| /** @type {?SDK.TracingManager} */ | |
| this._tracingManager = null; | |
| /** @type {?SDK.ResourceTreeModel} */ | |
| this._resourceTreeModel = null; | |
| this._timeCalculator = timeCalculator; | |
| this._filmStripView = filmStripView; | |
| /** @type {?SDK.TracingModel} */ | |
| this._tracingModel = null; | |
| /** @type {?function(?SDK.FilmStripModel)} */ | |
| this._callback = null; | |
| } | |
| /** | |
| * @override | |
| * @param {!Array.<!SDK.TracingManager.EventPayload>} events | |
| */ | |
| traceEventsCollected(events) { | |
| if (this._tracingModel) { | |
| this._tracingModel.addEvents(events); | |
| } | |
| } | |
| /** | |
| * @override | |
| */ | |
| tracingComplete() { | |
| if (!this._tracingModel || !this._tracingManager) { | |
| return; | |
| } | |
| this._tracingModel.tracingComplete(); | |
| this._tracingManager = null; | |
| this._callback(new SDK.FilmStripModel(this._tracingModel, this._timeCalculator.minimumBoundary() * 1000)); | |
| this._callback = null; | |
| if (this._resourceTreeModel) { | |
| this._resourceTreeModel.resumeReload(); | |
| } | |
| this._resourceTreeModel = null; | |
| } | |
| /** | |
| * @override | |
| */ | |
| tracingBufferUsage() { | |
| } | |
| /** | |
| * @override | |
| * @param {number} progress | |
| */ | |
| eventsRetrievalProgress(progress) { | |
| } | |
| startRecording() { | |
| this._filmStripView.reset(); | |
| this._filmStripView.setStatusText(Common.UIString('Recording frames...')); | |
| const tracingManagers = SDK.targetManager.models(SDK.TracingManager); | |
| if (this._tracingManager || !tracingManagers.length) { | |
| return; | |
| } | |
| this._tracingManager = tracingManagers[0]; | |
| this._resourceTreeModel = this._tracingManager.target().model(SDK.ResourceTreeModel); | |
| if (this._tracingModel) { | |
| this._tracingModel.dispose(); | |
| } | |
| this._tracingModel = new SDK.TracingModel(new Bindings.TempFileBackingStorage()); | |
| this._tracingManager.start(this, '-*,disabled-by-default-devtools.screenshot', ''); | |
| Host.userMetrics.actionTaken(Host.UserMetrics.Action.FilmStripStartedRecording); | |
| } | |
| /** | |
| * @return {boolean} | |
| */ | |
| isRecording() { | |
| return !!this._tracingManager; | |
| } | |
| /** | |
| * @param {function(?SDK.FilmStripModel)} callback | |
| */ | |
| stopRecording(callback) { | |
| if (!this._tracingManager) { | |
| return; | |
| } | |
| this._tracingManager.stop(); | |
| if (this._resourceTreeModel) { | |
| this._resourceTreeModel.suspendReload(); | |
| } | |
| this._callback = callback; | |
| this._filmStripView.setStatusText(Common.UIString('Fetching frames...')); | |
| } | |
| } | |
| /** | |
| * @implements {UI.ActionDelegate} | |
| */ | |
| export class ActionDelegate { | |
| /** | |
| * @override | |
| * @param {!UI.Context} context | |
| * @param {string} actionId | |
| * @return {boolean} | |
| */ | |
| handleAction(context, actionId) { | |
| const panel = UI.context.flavor(NetworkPanel); | |
| console.assert(panel && panel instanceof NetworkPanel); | |
| switch (actionId) { | |
| case 'network.toggle-recording': | |
| panel._toggleRecording(); | |
| return true; | |
| case 'network.hide-request-details': | |
| if (!panel._networkItemView) { | |
| return false; | |
| } | |
| panel._hideRequestPanel(); | |
| panel._networkLogView.resetFocus(); | |
| return true; | |
| case 'network.search': | |
| const selection = UI.inspectorView.element.window().getSelection(); | |
| let queryCandidate = ''; | |
| if (selection.rangeCount) { | |
| queryCandidate = selection.toString().replace(/\r?\n.*/, ''); | |
| } | |
| Network.SearchNetworkView.openSearch(queryCandidate); | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| /** | |
| * @implements {Common.Revealer} | |
| */ | |
| export class RequestLocationRevealer { | |
| /** | |
| * @override | |
| * @param {!Object} match | |
| * @return {!Promise} | |
| */ | |
| async reveal(match) { | |
| const location = /** @type {!Network.UIRequestLocation} */ (match); | |
| const view = await NetworkPanel._instance().selectRequest(location.request); | |
| if (!view) { | |
| return; | |
| } | |
| if (location.searchMatch) { | |
| await view.revealResponseBody(location.searchMatch.lineNumber); | |
| } | |
| if (location.requestHeader) { | |
| view.revealRequestHeader(location.requestHeader.name); | |
| } | |
| if (location.responseHeader) { | |
| view.revealResponseHeader(location.responseHeader.name); | |
| } | |
| } | |
| } | |
| export class SearchNetworkView extends Search.SearchView { | |
| constructor() { | |
| super('network'); | |
| } | |
| /** | |
| * @param {string} query | |
| * @param {boolean=} searchImmediately | |
| * @return {!Promise<!Search.SearchView>} | |
| */ | |
| static async openSearch(query, searchImmediately) { | |
| await UI.viewManager.showView('network.search-network-tab'); | |
| const searchView = | |
| /** @type {!Network.SearchNetworkView} */ (self.runtime.sharedInstance(Network.SearchNetworkView)); | |
| searchView.toggle(query, !!searchImmediately); | |
| return searchView; | |
| } | |
| /** | |
| * @override | |
| * @return {!Search.SearchScope} | |
| */ | |
| createScope() { | |
| return new Network.NetworkSearchScope(); | |
| } | |
| } | |
| /* Legacy exported object */ | |
| self.Network = self.Network || {}; | |
| /* Legacy exported object */ | |
| Network = Network || {}; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel = NetworkPanel; | |
| Network.NetworkPanel.displayScreenshotDelay = displayScreenshotDelay; | |
| /** | |
| * @constructor | |
| */ | |
| Network.SearchNetworkView = SearchNetworkView; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel.ContextMenuProvider = ContextMenuProvider; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel.RequestRevealer = RequestRevealer; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel.FilmStripRecorder = FilmStripRecorder; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel.ActionDelegate = ActionDelegate; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkPanel.RequestLocationRevealer = RequestLocationRevealer; |