mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-01 01:38:07 +08:00
For all comments that contained both the unrestricted annotation and any other potential annotation, remove only the unrestricted annotation. R=szuend@chromium.org No-Presubmit: True Bug: 1011811 Change-Id: I27dd67f657cdb1a9233d768eea81cabe2591f5f7 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2569759 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org>
1014 lines
34 KiB
JavaScript
1014 lines
34 KiB
JavaScript
/*
|
|
* 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.
|
|
*/
|
|
|
|
import * as Bindings from '../bindings/bindings.js';
|
|
import * as Common from '../common/common.js';
|
|
import * as Host from '../host/host.js';
|
|
import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
|
|
import * as PerfUI from '../perf_ui/perf_ui.js';
|
|
import * as SDK from '../sdk/sdk.js';
|
|
import * as Search from '../search/search.js';
|
|
import * as UI from '../ui/ui.js';
|
|
import * as Workspace from '../workspace/workspace.js';
|
|
|
|
import {BlockedURLsPane} from './BlockedURLsPane.js';
|
|
import {Events} from './NetworkDataGridNode.js';
|
|
import {NetworkItemView, Tabs as NetworkItemViewTabs} from './NetworkItemView.js'; // eslint-disable-line no-unused-vars
|
|
import {FilterType, NetworkLogView} from './NetworkLogView.js'; // eslint-disable-line no-unused-vars
|
|
import {NetworkOverview} from './NetworkOverview.js';
|
|
import {NetworkSearchScope, UIRequestLocation} from './NetworkSearchScope.js'; // eslint-disable-line no-unused-vars
|
|
import {NetworkTimeCalculator, NetworkTransferTimeCalculator} from './NetworkTimeCalculator.js'; // eslint-disable-line no-unused-vars
|
|
|
|
/** @type {!NetworkPanel} */
|
|
let networkPanelInstance;
|
|
|
|
/**
|
|
* @implements {UI.ContextMenu.Provider}
|
|
* @implements {UI.View.ViewLocationResolver}
|
|
*/
|
|
export class NetworkPanel extends UI.Panel.Panel {
|
|
constructor() {
|
|
super('network');
|
|
this.registerRequiredCSS('network/networkPanel.css', {enableLegacyPatching: true});
|
|
|
|
this._networkLogShowOverviewSetting =
|
|
Common.Settings.Settings.instance().createSetting('networkLogShowOverview', true);
|
|
this._networkLogLargeRowsSetting = Common.Settings.Settings.instance().createSetting('networkLogLargeRows', false);
|
|
this._networkRecordFilmStripSetting =
|
|
Common.Settings.Settings.instance().createSetting('networkRecordFilmStripSetting', false);
|
|
/** @type {!UI.ActionRegistration.Action }*/
|
|
this._toggleRecordAction = (UI.ActionRegistry.ActionRegistry.instance().action('network.toggle-recording'));
|
|
|
|
/** @type {number|undefined} */
|
|
this._pendingStopTimer;
|
|
/** @type {?NetworkItemView} */
|
|
this._networkItemView = null;
|
|
/** @type {?PerfUI.FilmStripView.FilmStripView} */
|
|
this._filmStripView = null;
|
|
/** @type {?FilmStripRecorder} */
|
|
this._filmStripRecorder = null;
|
|
/** @type {?SDK.NetworkRequest.NetworkRequest} */
|
|
this._currentRequest = null;
|
|
|
|
const panel = new UI.Widget.VBox();
|
|
|
|
const networkToolbarContainer = panel.contentElement.createChild('div', 'network-toolbar-container');
|
|
this._panelToolbar = new UI.Toolbar.Toolbar('', networkToolbarContainer);
|
|
this._rightToolbar = new UI.Toolbar.Toolbar('', networkToolbarContainer);
|
|
|
|
this._filterBar = new UI.FilterBar.FilterBar('networkPanel', true);
|
|
this._filterBar.show(panel.contentElement);
|
|
this._filterBar.addEventListener(UI.FilterBar.FilterBar.Events.Changed, this._handleFilterChanged.bind(this));
|
|
|
|
this._settingsPane = new UI.Widget.HBox();
|
|
this._settingsPane.element.classList.add('network-settings-pane');
|
|
this._settingsPane.show(panel.contentElement);
|
|
this._showSettingsPaneSetting =
|
|
Common.Settings.Settings.instance().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.TimelineOverviewPane('network');
|
|
this._overviewPane.addEventListener(
|
|
PerfUI.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this));
|
|
this._overviewPane.element.id = 'network-overview-panel';
|
|
this._networkOverview = new NetworkOverview();
|
|
this._overviewPane.setOverviewControls([this._networkOverview]);
|
|
this._overviewPlaceholderElement = panel.contentElement.createChild('div');
|
|
|
|
this._calculator = new NetworkTransferTimeCalculator();
|
|
|
|
this._splitWidget = new UI.SplitWidget.SplitWidget(true, false, 'networkPanelSplitViewState');
|
|
this._splitWidget.hideMain();
|
|
this._splitWidget.show(panel.contentElement);
|
|
|
|
panel.setDefaultFocusedChild(this._filterBar);
|
|
|
|
const initialSidebarWidth = 225;
|
|
const splitWidget = new UI.SplitWidget.SplitWidget(true, false, 'networkPanelSidebarState', initialSidebarWidth);
|
|
splitWidget.hideSidebar();
|
|
splitWidget.enableShowModeSaving();
|
|
splitWidget.show(this.element);
|
|
this._sidebarLocation = UI.ViewManager.ViewManager.instance().createTabbedLocation(async () => {
|
|
UI.ViewManager.ViewManager.instance().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.Toolbar.ToolbarButton(Common.UIString.UIString('Close'), 'largeicon-delete');
|
|
closeSidebar.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => splitWidget.hideSidebar());
|
|
tabbedPane.rightToolbar().appendToolbarItem(closeSidebar);
|
|
splitWidget.setSidebarWidget(tabbedPane);
|
|
splitWidget.setMainWidget(panel);
|
|
splitWidget.setDefaultFocusedChild(panel);
|
|
this.setDefaultFocusedChild(splitWidget);
|
|
|
|
this._progressBarContainer = document.createElement('div');
|
|
|
|
/** @type {!NetworkLogView} */
|
|
this._networkLogView =
|
|
new NetworkLogView(this._filterBar, this._progressBarContainer, this._networkLogLargeRowsSetting);
|
|
this._splitWidget.setSidebarWidget(this._networkLogView);
|
|
|
|
/** @type {!HTMLElement} */
|
|
this._fileSelectorElement = /** @type {!HTMLElement} */ (
|
|
UI.UIUtils.createFileSelectorElement(this._networkLogView.onLoadFromFile.bind(this._networkLogView)));
|
|
panel.element.appendChild(this._fileSelectorElement);
|
|
|
|
this._detailsWidget = new UI.Widget.VBox();
|
|
this._detailsWidget.element.classList.add('network-details-view');
|
|
this._splitWidget.setMainWidget(this._detailsWidget);
|
|
|
|
this._closeButtonElement = document.createElement('div', {is: 'dt-close-button'});
|
|
this._closeButtonElement.addEventListener('click', async () => {
|
|
const action = UI.ActionRegistry.ActionRegistry.instance().action('network.hide-request-details');
|
|
if (action) {
|
|
await action.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.Settings.Settings.instance().moduleSetting('network_log.preserve-log');
|
|
this._recordLogSetting = Common.Settings.Settings.instance().moduleSetting('network_log.record-log');
|
|
this._recordLogSetting.addChangeListener(({data}) => this._toggleRecord(data));
|
|
|
|
this._throttlingSelect = this._createThrottlingConditionsSelect();
|
|
this._setupToolbarButtons(splitWidget);
|
|
|
|
this._toggleRecord(this._recordLogSetting.get());
|
|
this._toggleShowOverview();
|
|
this._toggleLargerRequests();
|
|
this._toggleRecordFilmStrip();
|
|
this._updateUI();
|
|
|
|
SDK.SDKModel.TargetManager.instance().addModelListener(
|
|
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.WillReloadPage, this._willReloadPage,
|
|
this);
|
|
SDK.SDKModel.TargetManager.instance().addModelListener(
|
|
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this._load, this);
|
|
this._networkLogView.addEventListener(Events.RequestSelected, this._onRequestSelected, this);
|
|
this._networkLogView.addEventListener(Events.RequestActivated, this._onRequestActivated, this);
|
|
SDK.NetworkLog.NetworkLog.instance().addEventListener(
|
|
SDK.NetworkLog.Events.RequestAdded, this._onUpdateRequest, this);
|
|
SDK.NetworkLog.NetworkLog.instance().addEventListener(
|
|
SDK.NetworkLog.Events.RequestUpdated, this._onUpdateRequest, this);
|
|
SDK.NetworkLog.NetworkLog.instance().addEventListener(SDK.NetworkLog.Events.Reset, this._onNetworkLogReset, this);
|
|
}
|
|
|
|
/**
|
|
* @param {{forceNew: ?boolean}} opts
|
|
*/
|
|
static instance(opts = {forceNew: null}) {
|
|
const {forceNew} = opts;
|
|
if (!networkPanelInstance || forceNew) {
|
|
networkPanelInstance = new NetworkPanel();
|
|
}
|
|
|
|
return networkPanelInstance;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<{filterType: ?FilterType, filterValue: string}>} filters
|
|
*/
|
|
static revealAndFilter(filters) {
|
|
const panel = NetworkPanel._instance();
|
|
let filterString = '';
|
|
for (const filter of filters) {
|
|
if (filter.filterType) {
|
|
filterString += `${filter.filterType}:${filter.filterValue} `;
|
|
} else {
|
|
filterString += `${filter.filterValue} `;
|
|
}
|
|
}
|
|
panel._networkLogView.setTextFilterValue(filterString);
|
|
UI.ViewManager.ViewManager.instance().showView('network');
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest.NetworkRequest} request
|
|
* @param {!NetworkItemViewTabs} tab
|
|
* @param {!FilterOptions=} options - Optional parameters to change filter behavior
|
|
*/
|
|
static async selectAndShowRequest(request, tab, options) {
|
|
const panel = NetworkPanel._instance();
|
|
await panel.selectAndActivateRequest(request, tab, options);
|
|
}
|
|
|
|
/**
|
|
* @return {!NetworkPanel}
|
|
*/
|
|
static _instance() {
|
|
return NetworkPanel.instance();
|
|
}
|
|
|
|
/**
|
|
* @return {!UI.Toolbar.ToolbarComboBox}
|
|
*/
|
|
throttlingSelectForTest() {
|
|
return this._throttlingSelect;
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} 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);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
async _searchToggleClick(event) {
|
|
const action = UI.ActionRegistry.ActionRegistry.instance().action('network.search');
|
|
if (action) {
|
|
await action.execute();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.SplitWidget.SplitWidget} splitWidget
|
|
*/
|
|
_setupToolbarButtons(splitWidget) {
|
|
const searchToggle = new UI.Toolbar.ToolbarToggle(ls`Search`, 'largeicon-search');
|
|
function updateSidebarToggle() {
|
|
const isSidebarShowing = splitWidget.showMode() !== UI.SplitWidget.ShowMode.OnlyMain;
|
|
searchToggle.setToggled(isSidebarShowing);
|
|
if (!isSidebarShowing) {
|
|
/** @type {!HTMLElement} */ (searchToggle.element).focus();
|
|
}
|
|
}
|
|
this._panelToolbar.appendToolbarItem(UI.Toolbar.Toolbar.createActionButton(this._toggleRecordAction));
|
|
const clearButton = new UI.Toolbar.ToolbarButton(Common.UIString.UIString('Clear'), 'largeicon-clear');
|
|
clearButton.addEventListener(
|
|
UI.Toolbar.ToolbarButton.Events.Click, () => SDK.NetworkLog.NetworkLog.instance().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.Toolbar.ToolbarButton.Events.Click, event => {
|
|
this._searchToggleClick(event);
|
|
});
|
|
this._panelToolbar.appendToolbarItem(searchToggle);
|
|
this._panelToolbar.appendSeparator();
|
|
|
|
this._panelToolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
|
this._preserveLogSetting, Common.UIString.UIString('Do not clear log on page reload / navigation'),
|
|
Common.UIString.UIString('Preserve log')));
|
|
|
|
const disableCacheCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
|
|
Common.Settings.Settings.instance().moduleSetting('cacheDisabled'),
|
|
Common.UIString.UIString('Disable cache (while DevTools is open)'), Common.UIString.UIString('Disable cache'));
|
|
this._panelToolbar.appendToolbarItem(disableCacheCheckbox);
|
|
|
|
this._panelToolbar.appendSeparator();
|
|
this._panelToolbar.appendToolbarItem(this._throttlingSelect);
|
|
|
|
this._rightToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this._progressBarContainer));
|
|
this._rightToolbar.appendSeparator();
|
|
this._rightToolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingToggle(
|
|
this._showSettingsPaneSetting, 'largeicon-settings-gear', ls`Network settings`));
|
|
|
|
const settingsToolbarLeft = new UI.Toolbar.Toolbar('', this._settingsPane.element);
|
|
settingsToolbarLeft.makeVertical();
|
|
settingsToolbarLeft.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
|
this._networkLogLargeRowsSetting, ls`Show more information in request rows`, ls`Use large request rows`));
|
|
settingsToolbarLeft.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
|
this._networkLogShowOverviewSetting, ls`Show overview of network requests`, ls`Show overview`));
|
|
|
|
const settingsToolbarRight = new UI.Toolbar.Toolbar('', this._settingsPane.element);
|
|
settingsToolbarRight.makeVertical();
|
|
settingsToolbarRight.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
|
Common.Settings.Settings.instance().moduleSetting('network.group-by-frame'),
|
|
ls`Group requests by top level request frame`, ls`Group by frame`));
|
|
settingsToolbarRight.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
|
this._networkRecordFilmStripSetting, ls`Capture screenshots when loading a page`, ls`Capture screenshots`));
|
|
|
|
this._panelToolbar.appendSeparator();
|
|
const importHarButton = new UI.Toolbar.ToolbarButton(ls`Import HAR file...`, 'largeicon-load');
|
|
importHarButton.addEventListener(
|
|
UI.Toolbar.ToolbarButton.Events.Click, () => this._fileSelectorElement.click(), this);
|
|
this._panelToolbar.appendToolbarItem(importHarButton);
|
|
const exportHarButton = new UI.Toolbar.ToolbarButton(ls`Export HAR...`, 'largeicon-download');
|
|
exportHarButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, event => {
|
|
this._networkLogView.exportAll();
|
|
}, this);
|
|
this._panelToolbar.appendToolbarItem(exportHarButton);
|
|
}
|
|
|
|
_updateSettingsPaneVisibility() {
|
|
this._settingsPane.element.classList.toggle('hidden', !this._showSettingsPaneSetting.get());
|
|
}
|
|
|
|
/**
|
|
* @return {!UI.Toolbar.ToolbarComboBox}
|
|
*/
|
|
_createThrottlingConditionsSelect() {
|
|
const toolbarItem = new UI.Toolbar.ToolbarComboBox(null, ls`Throttling`);
|
|
toolbarItem.setMaxWidth(160);
|
|
MobileThrottling.ThrottlingManager.throttlingManager().decorateSelectWithNetworkThrottling(
|
|
toolbarItem.selectElement());
|
|
return toolbarItem;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} toggled
|
|
*/
|
|
_toggleRecord(toggled) {
|
|
this._toggleRecordAction.setToggled(toggled);
|
|
if (this._recordLogSetting.get() !== toggled) {
|
|
this._recordLogSetting.set(toggled);
|
|
}
|
|
|
|
this._networkLogView.setRecording(toggled);
|
|
if (!toggled && this._filmStripRecorder) {
|
|
this._filmStripRecorder.stopRecording(this._filmStripAvailable.bind(this));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {?SDK.FilmStripModel.FilmStripModel} filmStripModel
|
|
*/
|
|
_filmStripAvailable(filmStripModel) {
|
|
if (!filmStripModel) {
|
|
return;
|
|
}
|
|
const calculator = this._networkLogView.timeCalculator();
|
|
if (this._filmStripView) {
|
|
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() {
|
|
BlockedURLsPane.reset();
|
|
if (!this._preserveLogSetting.get()) {
|
|
this._calculator.reset();
|
|
this._overviewPane.reset();
|
|
}
|
|
if (this._filmStripView) {
|
|
this._resetFilmStripView();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_willReloadPage(event) {
|
|
if (this._pendingStopTimer) {
|
|
clearTimeout(this._pendingStopTimer);
|
|
delete this._pendingStopTimer;
|
|
}
|
|
if (this.isShowing() && this._filmStripRecorder) {
|
|
this._filmStripRecorder.startRecording();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_load(event) {
|
|
if (this._filmStripRecorder && this._filmStripRecorder.isRecording()) {
|
|
this._pendingStopTimer = window.setTimeout(this._stopFilmStripRecording.bind(this), displayScreenshotDelay);
|
|
}
|
|
}
|
|
|
|
_stopFilmStripRecording() {
|
|
if (this._filmStripRecorder) {
|
|
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.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) {
|
|
if (this._filmStripView) {
|
|
this._filmStripView.detach();
|
|
}
|
|
this._filmStripView = null;
|
|
this._filmStripRecorder = null;
|
|
}
|
|
}
|
|
|
|
_resetFilmStripView() {
|
|
const reloadShortcut =
|
|
UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction('inspector_main.reload')[0];
|
|
|
|
if (this._filmStripView) {
|
|
this._filmStripView.reset();
|
|
if (reloadShortcut) {
|
|
this._filmStripView.setStatusText(
|
|
Common.UIString.UIString('Hit %s to reload and capture filmstrip.', reloadShortcut.title()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Array.<!Element>}
|
|
*/
|
|
elementsToRestoreScrollPositionsFor() {
|
|
return this._networkLogView.elementsToRestoreScrollPositionsFor();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
wasShown() {
|
|
UI.Context.Context.instance().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.Context.instance().setFlavor(NetworkPanel, null);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest.NetworkRequest} request
|
|
*/
|
|
revealAndHighlightRequest(request) {
|
|
this._hideRequestPanel();
|
|
if (request) {
|
|
this._networkLogView.revealAndHighlightRequest(request);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest.NetworkRequest} request
|
|
* @param {!NetworkItemViewTabs=} shownTab
|
|
* @param {!FilterOptions=} options - Optional parameters to change filter behavior
|
|
* @return {!Promise<?NetworkItemView>}
|
|
*/
|
|
async selectAndActivateRequest(request, shownTab, options) {
|
|
await UI.ViewManager.ViewManager.instance().showView('network');
|
|
this._networkLogView.selectRequest(request, options);
|
|
this._showRequestPanel(shownTab);
|
|
return this._networkItemView;
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_handleFilterChanged(event) {
|
|
this._hideRequestPanel();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onRowSizeChanged(event) {
|
|
this._updateUI();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onRequestSelected(event) {
|
|
const request = /** @type {?SDK.NetworkRequest.NetworkRequest} */ (event.data);
|
|
this._currentRequest = request;
|
|
this._networkOverview.setHighlightedRequest(request);
|
|
this._updateNetworkItemView();
|
|
}
|
|
|
|
/**
|
|
* @param {!{data: *}} event
|
|
*/
|
|
_onRequestActivated(event) {
|
|
const eventData =
|
|
/** @type {!{showPanel: boolean, tab: !NetworkItemViewTabs, takeFocus: (boolean|undefined)}} */ (event.data);
|
|
if (eventData.showPanel) {
|
|
this._showRequestPanel(eventData.tab, /* takeFocus */ eventData.takeFocus);
|
|
} else {
|
|
this._hideRequestPanel();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!NetworkItemViewTabs=} shownTab
|
|
* @param {boolean=} takeFocus
|
|
*/
|
|
_showRequestPanel(shownTab, takeFocus) {
|
|
this._clearNetworkItemView();
|
|
if (this._currentRequest) {
|
|
const networkItemView = this._createNetworkItemView(shownTab);
|
|
if (networkItemView && takeFocus) {
|
|
networkItemView.focus();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
/**
|
|
* @param {!NetworkItemViewTabs=} initialTab
|
|
* @returns {(!NetworkItemView|undefined)}
|
|
*/
|
|
_createNetworkItemView(initialTab) {
|
|
if (!this._currentRequest) {
|
|
return;
|
|
}
|
|
this._networkItemView =
|
|
new NetworkItemView(this._currentRequest, this._networkLogView.timeCalculator(), initialTab);
|
|
this._networkItemView.leftToolbar().appendToolbarItem(new UI.Toolbar.ToolbarItem(this._closeButtonElement));
|
|
this._networkItemView.show(this._detailsWidget.element);
|
|
this._splitWidget.showBoth();
|
|
return this._networkItemView;
|
|
}
|
|
|
|
_updateUI() {
|
|
if (this._detailsWidget) {
|
|
this._detailsWidget.element.classList.toggle(
|
|
'network-details-view-tall-header', this._networkLogLargeRowsSetting.get());
|
|
}
|
|
if (this._networkLogView) {
|
|
this._networkLogView.switchViewMode(!this._splitWidget.isResizable());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!Event} event
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {!Object} target
|
|
* @this {NetworkPanel}
|
|
*/
|
|
appendApplicableItems(event, contextMenu, target) {
|
|
/**
|
|
* @this {NetworkPanel}
|
|
* @param {!SDK.NetworkRequest.NetworkRequest} request
|
|
*/
|
|
function reveal(request) {
|
|
UI.ViewManager.ViewManager.instance()
|
|
.showView('network')
|
|
.then(this._networkLogView.resetFilter.bind(this._networkLogView))
|
|
.then(this.revealAndHighlightRequest.bind(this, request));
|
|
}
|
|
|
|
/**
|
|
* @this {NetworkPanel}
|
|
* @param {!SDK.NetworkRequest.NetworkRequest} request
|
|
*/
|
|
function appendRevealItem(request) {
|
|
contextMenu.revealSection().appendItem(
|
|
Common.UIString.UIString('Reveal in Network panel'), reveal.bind(this, request));
|
|
}
|
|
|
|
if (/** @type {!Node} */ (event.target).isSelfOrDescendant(this.element)) {
|
|
return;
|
|
}
|
|
|
|
if (target instanceof SDK.Resource.Resource) {
|
|
const resource = /** @type {!SDK.Resource.Resource} */ (target);
|
|
if (resource.request) {
|
|
appendRevealItem.call(this, resource.request);
|
|
}
|
|
return;
|
|
}
|
|
if (target instanceof Workspace.UISourceCode.UISourceCode) {
|
|
const uiSourceCode = /** @type {!Workspace.UISourceCode.UISourceCode} */ (target);
|
|
const resource = Bindings.ResourceUtils.resourceForURL(uiSourceCode.url());
|
|
if (resource && resource.request) {
|
|
appendRevealItem.call(this, resource.request);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!(target instanceof SDK.NetworkRequest.NetworkRequest)) {
|
|
return;
|
|
}
|
|
const request = /** @type {!SDK.NetworkRequest.NetworkRequest} */ (target);
|
|
if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request) {
|
|
return;
|
|
}
|
|
|
|
appendRevealItem.call(this, request);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onFilmFrameSelected(event) {
|
|
const timestamp = /** @type {number} */ (event.data);
|
|
this._overviewPane.setWindowTimes(0, timestamp);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onFilmFrameEnter(event) {
|
|
const timestamp = /** @type {number} */ (event.data);
|
|
this._networkOverview.selectFilmStripFrame(timestamp);
|
|
this._networkLogView.selectFilmStripFrame(timestamp / 1000);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onFilmFrameExit(event) {
|
|
this._networkOverview.clearFilmStripFrame();
|
|
this._networkLogView.clearFilmStripFrame();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_onUpdateRequest(event) {
|
|
const request = /** @type {!SDK.NetworkRequest.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.View.ViewLocation}
|
|
*/
|
|
resolveLocation(locationName) {
|
|
if (locationName === 'network-sidebar') {
|
|
return this._sidebarLocation;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export const displayScreenshotDelay = 1000;
|
|
|
|
/**
|
|
* @implements {UI.ContextMenu.Provider}
|
|
*/
|
|
export class ContextMenuProvider {
|
|
/**
|
|
* @override
|
|
* @param {!Event} event
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {!Object} target
|
|
*/
|
|
appendApplicableItems(event, contextMenu, target) {
|
|
NetworkPanel._instance().appendApplicableItems(event, contextMenu, target);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @implements {Common.Revealer.Revealer}
|
|
*/
|
|
export class RequestRevealer {
|
|
/**
|
|
* @override
|
|
* @param {!Object} request
|
|
* @return {!Promise<void>}
|
|
*/
|
|
reveal(request) {
|
|
if (!(request instanceof SDK.NetworkRequest.NetworkRequest)) {
|
|
return Promise.reject(new Error('Internal error: not a network request'));
|
|
}
|
|
const panel = NetworkPanel._instance();
|
|
return UI.ViewManager.ViewManager.instance().showView('network').then(
|
|
panel.revealAndHighlightRequest.bind(panel, request));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @implements {SDK.TracingManager.TracingManagerClient}
|
|
*/
|
|
export class FilmStripRecorder {
|
|
/**
|
|
* @param {!NetworkTimeCalculator} timeCalculator
|
|
* @param {!PerfUI.FilmStripView.FilmStripView} filmStripView
|
|
*/
|
|
constructor(timeCalculator, filmStripView) {
|
|
/** @type {?SDK.TracingManager.TracingManager} */
|
|
this._tracingManager = null;
|
|
/** @type {?SDK.ResourceTreeModel.ResourceTreeModel} */
|
|
this._resourceTreeModel = null;
|
|
this._timeCalculator = timeCalculator;
|
|
this._filmStripView = filmStripView;
|
|
/** @type {?SDK.TracingModel.TracingModel} */
|
|
this._tracingModel = null;
|
|
/** @type {?function(?SDK.FilmStripModel.FilmStripModel):void} */
|
|
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;
|
|
if (this._callback) {
|
|
this._callback(
|
|
new SDK.FilmStripModel.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.UIString('Recording frames...'));
|
|
const tracingManagers = SDK.SDKModel.TargetManager.instance().models(SDK.TracingManager.TracingManager);
|
|
if (this._tracingManager || !tracingManagers.length) {
|
|
return;
|
|
}
|
|
|
|
this._tracingManager = tracingManagers[0];
|
|
if (!this._tracingManager) {
|
|
return;
|
|
}
|
|
this._resourceTreeModel = this._tracingManager.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
if (this._tracingModel) {
|
|
this._tracingModel.dispose();
|
|
}
|
|
this._tracingModel = new SDK.TracingModel.TracingModel(new Bindings.TempFile.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.FilmStripModel):void} callback
|
|
*/
|
|
stopRecording(callback) {
|
|
if (!this._tracingManager) {
|
|
return;
|
|
}
|
|
|
|
this._tracingManager.stop();
|
|
if (this._resourceTreeModel) {
|
|
this._resourceTreeModel.suspendReload();
|
|
}
|
|
this._callback = callback;
|
|
this._filmStripView.setStatusText(Common.UIString.UIString('Fetching frames...'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @implements {UI.ActionRegistration.ActionDelegate}
|
|
*/
|
|
export class ActionDelegate {
|
|
/**
|
|
* @override
|
|
* @param {!UI.Context.Context} context
|
|
* @param {string} actionId
|
|
* @return {boolean}
|
|
*/
|
|
handleAction(context, actionId) {
|
|
const panel = UI.Context.Context.instance().flavor(NetworkPanel);
|
|
console.assert(!!(panel && panel instanceof NetworkPanel));
|
|
if (!panel) {
|
|
return false;
|
|
}
|
|
switch (actionId) {
|
|
case 'network.toggle-recording': {
|
|
panel._toggleRecord(!panel._recordLogSetting.get());
|
|
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.InspectorView.instance().element.window().getSelection();
|
|
if (selection) {
|
|
let queryCandidate = '';
|
|
if (selection.rangeCount) {
|
|
queryCandidate = selection.toString().replace(/\r?\n.*/, '');
|
|
}
|
|
SearchNetworkView.openSearch(queryCandidate);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @implements {Common.Revealer.Revealer}
|
|
*/
|
|
export class RequestLocationRevealer {
|
|
/**
|
|
* @override
|
|
* @param {!Object} match
|
|
* @return {!Promise<void>}
|
|
*/
|
|
async reveal(match) {
|
|
const location = /** @type {!UIRequestLocation} */ (match);
|
|
const view = await NetworkPanel._instance().selectAndActivateRequest(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @type {!SearchNetworkView} */
|
|
let searchNetworkViewInstance;
|
|
|
|
export class SearchNetworkView extends Search.SearchView.SearchView {
|
|
/**
|
|
* @private
|
|
*/
|
|
constructor() {
|
|
super('network');
|
|
}
|
|
|
|
/**
|
|
* @param {{forceNew: ?boolean}} opts
|
|
*/
|
|
static instance(opts = {forceNew: null}) {
|
|
const {forceNew} = opts;
|
|
if (!searchNetworkViewInstance || forceNew) {
|
|
searchNetworkViewInstance = new SearchNetworkView();
|
|
}
|
|
|
|
return searchNetworkViewInstance;
|
|
}
|
|
|
|
/**
|
|
* @param {string} query
|
|
* @param {boolean=} searchImmediately
|
|
* @return {!Promise<!Search.SearchView.SearchView>}
|
|
*/
|
|
static async openSearch(query, searchImmediately) {
|
|
await UI.ViewManager.ViewManager.instance().showView('network.search-network-tab');
|
|
const searchView = SearchNetworkView.instance();
|
|
searchView.toggle(query, !!searchImmediately);
|
|
return searchView;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Search.SearchConfig.SearchScope}
|
|
*/
|
|
createScope() {
|
|
return new NetworkSearchScope();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef {{
|
|
* clearFilter: boolean,
|
|
* }}
|
|
*/
|
|
// @ts-ignore typedef
|
|
export let FilterOptions;
|