@quick-game/cli
Version:
Command line interface for rapid qg development
1,039 lines • 98.9 kB
JavaScript
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* 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 Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as Root from '../../core/root/root.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Bindings from '../../models/bindings/bindings.js';
import * as HAR from '../../models/har/har.js';
import * as Logs from '../../models/logs/logs.js';
import * as Persistence from '../../models/persistence/persistence.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';
import * as NetworkForward from '../../panels/network/forward/forward.js';
import * as Sources from '../../panels/sources/sources.js';
import * as Coordinator from '../../ui/components/render_coordinator/render_coordinator.js';
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
import * as Components from '../../ui/legacy/components/utils/utils.js';
import * as UI from '../../ui/legacy/legacy.js';
import networkLogViewStyles from './networkLogView.css.js';
import { Events, NetworkGroupNode, NetworkRequestNode, } from './NetworkDataGridNode.js';
import { NetworkFrameGrouper } from './NetworkFrameGrouper.js';
import { NetworkLogViewColumns } from './NetworkLogViewColumns.js';
import { NetworkTimeBoundary, NetworkTransferDurationCalculator, NetworkTransferTimeCalculator, } from './NetworkTimeCalculator.js';
const UIStrings = {
/**
*@description Text in Network Log View of the Network panel
*/
invertFilter: 'Invert',
/**
*@description Tooltip for the 'invert' checkbox in the Network panel.
*/
invertsFilter: 'Inverts the search filter',
/**
*@description Text in Network Log View of the Network panel
*/
hideDataUrls: 'Hide data URLs',
/**
*@description Data urlfilter ui element title in Network Log View of the Network panel
*/
hidesDataAndBlobUrls: 'Hide \'data:\' and \'blob:\' URLs',
/**
* @description Label for a filter in the Network panel
*/
chromeExtensions: 'Hide extension URLs',
/**
* @description Tooltip for a filter in the Network panel
*/
hideChromeExtension: 'Hide \'chrome-extension://\' URLs',
/**
*@description Aria accessible name in Network Log View of the Network panel
*/
resourceTypesToInclude: 'Resource types to include',
/**
*@description Label for a checkbox in the Network panel. When checked, only requests with
* blocked response cookies are shown.
*/
hasBlockedCookies: 'Blocked response cookies',
/**
*@description Tooltip for a checkbox in the Network panel. The response to a network request may include a
* cookie (https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies). Such response cookies can
* be malformed or otherwise invalid and the browser may choose to ignore or not accept invalid cookies.
*/
onlyShowRequestsWithBlockedCookies: 'Show only the requests with blocked response cookies',
/**
*@description Label for a filter in the Network panel
*/
blockedRequests: 'Blocked requests',
/**
*@description Tooltip for a filter in the Network panel
*/
onlyShowBlockedRequests: 'Show only blocked requests',
/**
*@description Label for a filter in the Network panel
*/
thirdParty: '3rd-party requests',
/**
*@description Tooltip for a filter in the Network panel
*/
onlyShowThirdPartyRequests: 'Show only requests with origin different from page origin',
/**
*@description Text that appears when user drag and drop something (for example, a file) in Network Log View of the Network panel
*/
dropHarFilesHere: 'Drop HAR files here',
/**
*@description Recording text text content in Network Log View of the Network panel
*/
recordingNetworkActivity: 'Recording network activity…',
/**
*@description Text in Network Log View of the Network panel
*@example {Ctrl + R} PH1
*/
performARequestOrHitSToRecordThe: 'Perform a request or hit {PH1} to record the reload.',
/**
*@description Shown in the Network Log View of the Network panel when the user has not yet
* recorded any network activity. This is an instruction to the user to start recording in order to
* show network activity in the current UI.
*@example {Ctrl + E} PH1
*/
recordToDisplayNetworkActivity: 'Record network log ({PH1}) to display network activity.',
/**
*@description Text that is usually a hyperlink to more documentation
*/
learnMore: 'Learn more',
/**
*@description Text to announce to screen readers that network data is available.
*/
networkDataAvailable: 'Network Data Available',
/**
*@description Text in Network Log View of the Network panel
*@example {3} PH1
*@example {5} PH2
*/
sSRequests: '{PH1} / {PH2} requests',
/**
*@description Message in the summary toolbar at the bottom of the Network log that shows the compressed size of the
* resources transferred during a selected time frame over the compressed size of all resources transferred during
* the whole network log.
*@example {5 B} PH1
*@example {10 B} PH2
*/
sSTransferred: '{PH1} / {PH2} transferred',
/**
*@description Message in a tooltip that shows the compressed size of the resources transferred during a selected
* time frame over the compressed size of all resources transferred during the whole network log.
*@example {10} PH1
*@example {15} PH2
*/
sBSBTransferredOverNetwork: '{PH1} B / {PH2} B transferred over network',
/**
* @description Text in Network Log View of the Network panel. Appears when a particular network
* resource is selected by the user. Shows how large the selected resource was (PH1) out of the
* total size (PH2).
* @example {40MB} PH1
* @example {50MB} PH2
*/
sSResources: '{PH1} / {PH2} resources',
/**
*@description Text in Network Log View of the Network panel
*@example {40} PH1
*@example {50} PH2
*/
sBSBResourcesLoadedByThePage: '{PH1} B / {PH2} B resources loaded by the page',
/**
*@description Text in Network Log View of the Network panel
*@example {6} PH1
*/
sRequests: '{PH1} requests',
/**
*@description Message in the summary toolbar at the bottom of the Network log that shows the compressed size of
* all resources transferred over network during a network activity log.
*@example {4 B} PH1
*/
sTransferred: '{PH1} transferred',
/**
*@description Message in a tooltip that shows the compressed size of all resources transferred over network during
* a network activity log.
*@example {4} PH1
*/
sBTransferredOverNetwork: '{PH1} B transferred over network',
/**
*@description Text in Network Log View of the Network panel
*@example {4} PH1
*/
sResources: '{PH1} resources',
/**
*@description Text in Network Log View of the Network panel
*@example {10} PH1
*/
sBResourcesLoadedByThePage: '{PH1} B resources loaded by the page',
/**
*@description Text in Network Log View of the Network panel
*@example {120ms} PH1
*/
finishS: 'Finish: {PH1}',
/**
*@description Text in Network Log View of the Network panel
*@example {3000ms} PH1
*/
domcontentloadedS: 'DOMContentLoaded: {PH1}',
/**
*@description Text in Network Log View of the Network panel
*@example {40ms} PH1
*/
loadS: 'Load: {PH1}',
/**
*@description Text for copying
*/
copy: 'Copy',
/**
*@description Text in Network Log View of the Network panel
*/
copyRequestHeaders: 'Copy request headers',
/**
*@description Text in Network Log View of the Network panel
*/
copyResponseHeaders: 'Copy response headers',
/**
*@description Text in Network Log View of the Network panel
*/
copyResponse: 'Copy response',
/**
*@description Text in Network Log View of the Network panel
*/
copyStacktrace: 'Copy stack trace',
/**
* @description A context menu command in the Network panel, for copying to the clipboard.
* PowerShell refers to the format the data will be copied as.
*/
copyAsPowershell: 'Copy as `PowerShell`',
/**
*@description A context menu command in the Network panel, for copying to the clipboard. 'fetch'
* refers to the format the data will be copied as, which is compatible with the fetch web API.
*/
copyAsFetch: 'Copy as `fetch`',
/**
* @description Text in Network Log View of the Network panel. An action that copies a command to
* the developer's clipboard. The command allows the developer to replay this specific network
* request in Node.js, a desktop application/framework. 'Node.js fetch' is a noun phrase for the
* type of request that will be copied.
*/
copyAsNodejsFetch: 'Copy as `Node.js` `fetch`',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with cURL (a program, not
*translatable).
*/
copyAsCurlCmd: 'Copy as `cURL` (`cmd`)',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with a Bash script.
*/
copyAsCurlBash: 'Copy as `cURL` (`bash`)',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with a PowerShell script.
*/
copyAllAsPowershell: 'Copy all as `PowerShell`',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with a 'fetch' command (fetch
*should not be translated).
*/
copyAllAsFetch: 'Copy all as `fetch`',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with a Node.js 'fetch' command
*(fetch and Node.js should not be translated).
*/
copyAllAsNodejsFetch: 'Copy all as `Node.js` `fetch`',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with cURL (a program, not
*translatable).
*/
copyAllAsCurlCmd: 'Copy all as `cURL` (`cmd`)',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with a Bash script.
*/
copyAllAsCurlBash: 'Copy all as `cURL` (`bash`)',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with cURL (a program, not
*translatable).
*/
copyAsCurl: 'Copy as `cURL`',
/**
*@description Text in Network Log View of the Network panel. An action that copies a command to
*the clipboard. It will copy the command in the format compatible with cURL (a program, not
*translatable).
*/
copyAllAsCurl: 'Copy all as `cURL`',
/**
* @description Text in Network Log View of the Network panel. An action that copies data to the
* clipboard. It will copy the data in the HAR (not translatable) format. 'all' refers to every
* network request that is currently shown.
*/
copyAllAsHar: 'Copy all as `HAR`',
/**
*@description A context menu item in the Network Log View of the Network panel
*/
saveAllAsHarWithContent: 'Save all as `HAR` with content',
/**
*@description A context menu item in the Network Log View of the Network panel
*/
clearBrowserCache: 'Clear browser cache',
/**
*@description A context menu item in the Network Log View of the Network panel
*/
clearBrowserCookies: 'Clear browser cookies',
/**
*@description A context menu item in the Network Log View of the Network panel
*/
blockRequestUrl: 'Block request URL',
/**
*@description A context menu item in the Network Log View of the Network panel
*@example {example.com} PH1
*/
unblockS: 'Unblock {PH1}',
/**
*@description A context menu item in the Network Log View of the Network panel
*/
blockRequestDomain: 'Block request domain',
/**
*@description Text to replay an XHR request
*/
replayXhr: 'Replay XHR',
/**
*@description Text in Network Log View of the Network panel
*/
areYouSureYouWantToClearBrowser: 'Are you sure you want to clear browser cache?',
/**
*@description Text in Network Log View of the Network panel
*/
areYouSureYouWantToClearBrowserCookies: 'Are you sure you want to clear browser cookies?',
/**
*@description A context menu item in the Network Log View of the Network panel
* for creating a header override
*/
overrideHeaders: 'Override headers',
};
const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkLogView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();
export class NetworkLogView extends Common.ObjectWrapper.eventMixin(UI.Widget.VBox) {
networkInvertFilterSetting;
networkHideDataURLSetting;
networkHideChromeExtensions;
networkShowBlockedCookiesOnlySetting;
networkOnlyBlockedRequestsSetting;
networkOnlyThirdPartySetting;
networkResourceTypeFiltersSetting;
rawRowHeight;
progressBarContainer;
networkLogLargeRowsSetting;
rowHeightInternal;
timeCalculatorInternal;
durationCalculator;
calculatorInternal;
columnsInternal;
staleRequests;
mainRequestLoadTime;
mainRequestDOMContentLoadedTime;
filters;
timeFilter;
hoveredNodeInternal;
recordingHint;
highlightedNode;
linkifierInternal;
recording;
needsRefresh;
headerHeightInternal;
groupLookups;
activeGroupLookup;
textFilterUI;
invertFilterUI;
dataURLFilterUI;
resourceCategoryFilterUI;
onlyBlockedResponseCookiesFilterUI;
onlyBlockedRequestsUI;
onlyThirdPartyFilterUI;
hideChromeExtensionsUI;
filterParser;
suggestionBuilder;
dataGrid;
summaryToolbarInternal;
filterBar;
textFilterSetting;
constructor(filterBar, progressBarContainer, networkLogLargeRowsSetting) {
super();
this.setMinimumSize(50, 64);
this.element.id = 'network-container';
this.element.classList.add('no-node-selected');
this.networkInvertFilterSetting = Common.Settings.Settings.instance().createSetting('networkInvertFilter', false);
this.networkHideDataURLSetting = Common.Settings.Settings.instance().createSetting('networkHideDataURL', false);
this.networkHideChromeExtensions =
Common.Settings.Settings.instance().createSetting('networkHideChromeExtensions', false);
this.networkShowBlockedCookiesOnlySetting =
Common.Settings.Settings.instance().createSetting('networkShowBlockedCookiesOnlySetting', false);
this.networkOnlyBlockedRequestsSetting =
Common.Settings.Settings.instance().createSetting('networkOnlyBlockedRequests', false);
this.networkOnlyThirdPartySetting =
Common.Settings.Settings.instance().createSetting('networkOnlyThirdPartySetting', false);
this.networkResourceTypeFiltersSetting =
Common.Settings.Settings.instance().createSetting('networkResourceTypeFilters', {});
this.rawRowHeight = 0;
this.progressBarContainer = progressBarContainer;
this.networkLogLargeRowsSetting = networkLogLargeRowsSetting;
this.networkLogLargeRowsSetting.addChangeListener(updateRowHeight.bind(this), this);
function updateRowHeight() {
this.rawRowHeight = Boolean(this.networkLogLargeRowsSetting.get()) ? 41 : 21;
this.rowHeightInternal = this.computeRowHeight();
}
this.rawRowHeight = 0;
this.rowHeightInternal = 0;
updateRowHeight.call(this);
this.timeCalculatorInternal = new NetworkTransferTimeCalculator();
this.durationCalculator = new NetworkTransferDurationCalculator();
this.calculatorInternal = this.timeCalculatorInternal;
this.columnsInternal = new NetworkLogViewColumns(this, this.timeCalculatorInternal, this.durationCalculator, networkLogLargeRowsSetting);
this.columnsInternal.show(this.element);
this.staleRequests = new Set();
this.mainRequestLoadTime = -1;
this.mainRequestDOMContentLoadedTime = -1;
this.filters = [];
this.timeFilter = null;
this.hoveredNodeInternal = null;
this.recordingHint = null;
this.highlightedNode = null;
this.linkifierInternal = new Components.Linkifier.Linkifier();
this.recording = false;
this.needsRefresh = false;
this.headerHeightInternal = 0;
this.groupLookups = new Map();
this.groupLookups.set('Frame', new NetworkFrameGrouper(this));
this.activeGroupLookup = null;
this.textFilterUI = new UI.FilterBar.TextFilterUI();
this.textFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged, this);
filterBar.addFilter(this.textFilterUI);
this.invertFilterUI = new UI.FilterBar.CheckboxFilterUI('invert-filter', i18nString(UIStrings.invertFilter), true, this.networkInvertFilterSetting);
this.invertFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.invertFilterUI.element(), i18nString(UIStrings.invertsFilter));
filterBar.addFilter(this.invertFilterUI);
this.dataURLFilterUI = new UI.FilterBar.CheckboxFilterUI('hide-data-url', i18nString(UIStrings.hideDataUrls), true, this.networkHideDataURLSetting);
this.dataURLFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.dataURLFilterUI.element(), i18nString(UIStrings.hidesDataAndBlobUrls));
filterBar.addFilter(this.dataURLFilterUI);
this.hideChromeExtensionsUI = new UI.FilterBar.CheckboxFilterUI('chrome-extension', i18nString(UIStrings.chromeExtensions), true, this.networkHideChromeExtensions);
this.hideChromeExtensionsUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.hideChromeExtensionsUI.element(), i18nString(UIStrings.hideChromeExtension));
filterBar.addFilter(this.hideChromeExtensionsUI);
const filterItems = Object.values(Common.ResourceType.resourceCategories)
.map(category => ({ name: category.title(), label: () => category.shortTitle(), title: category.title() }));
this.resourceCategoryFilterUI =
new UI.FilterBar.NamedBitSetFilterUI(filterItems, this.networkResourceTypeFiltersSetting);
UI.ARIAUtils.setLabel(this.resourceCategoryFilterUI.element(), i18nString(UIStrings.resourceTypesToInclude));
this.resourceCategoryFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
filterBar.addFilter(this.resourceCategoryFilterUI);
this.onlyBlockedResponseCookiesFilterUI = new UI.FilterBar.CheckboxFilterUI('only-show-blocked-cookies', i18nString(UIStrings.hasBlockedCookies), true, this.networkShowBlockedCookiesOnlySetting);
this.onlyBlockedResponseCookiesFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.onlyBlockedResponseCookiesFilterUI.element(), i18nString(UIStrings.onlyShowRequestsWithBlockedCookies));
filterBar.addFilter(this.onlyBlockedResponseCookiesFilterUI);
this.onlyBlockedRequestsUI = new UI.FilterBar.CheckboxFilterUI('only-show-blocked-requests', i18nString(UIStrings.blockedRequests), true, this.networkOnlyBlockedRequestsSetting);
this.onlyBlockedRequestsUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.onlyBlockedRequestsUI.element(), i18nString(UIStrings.onlyShowBlockedRequests));
filterBar.addFilter(this.onlyBlockedRequestsUI);
this.onlyThirdPartyFilterUI = new UI.FilterBar.CheckboxFilterUI('only-show-third-party', i18nString(UIStrings.thirdParty), true, this.networkOnlyThirdPartySetting);
this.onlyThirdPartyFilterUI.addEventListener("FilterChanged" /* UI.FilterBar.FilterUIEvents.FilterChanged */, this.filterChanged.bind(this), this);
UI.Tooltip.Tooltip.install(this.onlyThirdPartyFilterUI.element(), i18nString(UIStrings.onlyShowThirdPartyRequests));
filterBar.addFilter(this.onlyThirdPartyFilterUI);
this.filterParser = new TextUtils.TextUtils.FilterParser(searchKeys);
this.suggestionBuilder =
new UI.FilterSuggestionBuilder.FilterSuggestionBuilder(searchKeys, NetworkLogView.sortSearchValues);
this.resetSuggestionBuilder();
this.dataGrid = this.columnsInternal.dataGrid();
this.setupDataGrid();
this.columnsInternal.sortByCurrentColumn();
filterBar.filterButton().addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.dataGrid.scheduleUpdate.bind(this.dataGrid, true /* isFromUser */));
this.summaryToolbarInternal = new UI.Toolbar.Toolbar('network-summary-bar', this.element);
this.summaryToolbarInternal.element.setAttribute('role', 'status');
new UI.DropTarget.DropTarget(this.element, [UI.DropTarget.Type.File], i18nString(UIStrings.dropHarFilesHere), this.handleDrop.bind(this));
Common.Settings.Settings.instance()
.moduleSetting('networkColorCodeResourceTypes')
.addChangeListener(this.invalidateAllItems.bind(this, false), this);
SDK.TargetManager.TargetManager.instance().observeModels(SDK.NetworkManager.NetworkManager, this, { scoped: true });
Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.RequestAdded, this.onRequestUpdated, this);
Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.RequestUpdated, this.onRequestUpdated, this);
Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.RequestRemoved, this.onRequestRemoved, this);
Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.Reset, this.reset, this);
this.updateGroupByFrame();
Common.Settings.Settings.instance()
.moduleSetting('network.group-by-frame')
.addChangeListener(() => this.updateGroupByFrame());
this.filterBar = filterBar;
this.textFilterSetting = Common.Settings.Settings.instance().createSetting('networkTextFilter', '');
if (this.textFilterSetting.get()) {
this.textFilterUI.setValue(this.textFilterSetting.get());
}
}
updateGroupByFrame() {
const value = Common.Settings.Settings.instance().moduleSetting('network.group-by-frame').get();
this.setGrouping(value ? 'Frame' : null);
}
static sortSearchValues(key, values) {
if (key === NetworkForward.UIFilter.FilterType.Priority) {
values.sort((a, b) => {
const aPriority = PerfUI.NetworkPriorities.uiLabelToNetworkPriority(a);
const bPriority = PerfUI.NetworkPriorities.uiLabelToNetworkPriority(b);
return PerfUI.NetworkPriorities.networkPriorityWeight(aPriority) -
PerfUI.NetworkPriorities.networkPriorityWeight(bPriority);
});
}
else {
values.sort();
}
}
static negativeFilter(filter, request) {
return !filter(request);
}
static requestPathFilter(regex, request) {
if (!regex) {
return false;
}
return regex.test(request.path() + '/' + request.name());
}
static subdomains(domain) {
const result = [domain];
let indexOfPeriod = domain.indexOf('.');
while (indexOfPeriod !== -1) {
result.push('*' + domain.substring(indexOfPeriod));
indexOfPeriod = domain.indexOf('.', indexOfPeriod + 1);
}
return result;
}
static createRequestDomainFilter(value) {
const escapedPattern = value.split('*').map(Platform.StringUtilities.escapeForRegExp).join('.*');
return NetworkLogView.requestDomainFilter.bind(null, new RegExp('^' + escapedPattern + '$', 'i'));
}
static requestDomainFilter(regex, request) {
return regex.test(request.domain);
}
static runningRequestFilter(request) {
return !request.finished;
}
static fromCacheRequestFilter(request) {
return request.cached();
}
static interceptedByServiceWorkerFilter(request) {
return request.fetchedViaServiceWorker;
}
static initiatedByServiceWorkerFilter(request) {
return request.initiatedByServiceWorker();
}
static requestResponseHeaderFilter(value, request) {
return request.responseHeaderValue(value) !== undefined;
}
static requestResponseHeaderSetCookieFilter(value, request) {
// Multiple Set-Cookie headers in the request are concatenated via space. Only
// filter via `includes` instead of strict equality.
return Boolean(request.responseHeaderValue('Set-Cookie')?.includes(value));
}
static requestMethodFilter(value, request) {
return request.requestMethod === value;
}
static requestPriorityFilter(value, request) {
return request.priority() === value;
}
static requestMimeTypeFilter(value, request) {
return request.mimeType === value;
}
static requestMixedContentFilter(value, request) {
if (value === NetworkForward.UIFilter.MixedContentFilterValues.Displayed) {
return request.mixedContentType === "optionally-blockable" /* Protocol.Security.MixedContentType.OptionallyBlockable */;
}
if (value === NetworkForward.UIFilter.MixedContentFilterValues.Blocked) {
return request.mixedContentType === "blockable" /* Protocol.Security.MixedContentType.Blockable */ && request.wasBlocked();
}
if (value === NetworkForward.UIFilter.MixedContentFilterValues.BlockOverridden) {
return request.mixedContentType === "blockable" /* Protocol.Security.MixedContentType.Blockable */ && !request.wasBlocked();
}
if (value === NetworkForward.UIFilter.MixedContentFilterValues.All) {
return request.mixedContentType !== "none" /* Protocol.Security.MixedContentType.None */;
}
return false;
}
static requestSchemeFilter(value, request) {
return request.scheme === value;
}
static requestCookieDomainFilter(value, request) {
return request.allCookiesIncludingBlockedOnes().some(cookie => cookie.domain() === value);
}
static requestCookieNameFilter(value, request) {
return request.allCookiesIncludingBlockedOnes().some(cookie => cookie.name() === value);
}
static requestCookiePathFilter(value, request) {
return request.allCookiesIncludingBlockedOnes().some(cookie => cookie.path() === value);
}
static requestCookieValueFilter(value, request) {
return request.allCookiesIncludingBlockedOnes().some(cookie => cookie.value() === value);
}
static requestSetCookieDomainFilter(value, request) {
return request.responseCookies.some(cookie => cookie.domain() === value);
}
static requestSetCookieNameFilter(value, request) {
return request.responseCookies.some(cookie => cookie.name() === value);
}
static requestSetCookieValueFilter(value, request) {
return request.responseCookies.some(cookie => cookie.value() === value);
}
static requestSizeLargerThanFilter(value, request) {
return request.transferSize >= value;
}
static statusCodeFilter(value, request) {
return (String(request.statusCode)) === value;
}
static hasOverridesFilter(value, request) {
if (!value) {
return false;
}
if (value === overrideFilter.no) {
return request.overrideTypes.length === 0;
}
if (value === overrideFilter.yes) {
return request.overrideTypes.length > 0;
}
if (value === overrideFilter.content) {
return request.overrideTypes.includes('content');
}
if (value === overrideFilter.headers) {
return request.overrideTypes.includes('headers');
}
return request.overrideTypes.join(',').includes(value);
}
static getHTTPRequestsFilter(request) {
return request.parsedURL.isValid && (request.scheme in HTTPSchemas);
}
static resourceTypeFilter(value, request) {
return request.resourceType().name() === value;
}
static requestUrlFilter(value, request) {
const regex = new RegExp(Platform.StringUtilities.escapeForRegExp(value), 'i');
return regex.test(request.url());
}
static requestTimeFilter(windowStart, windowEnd, request) {
if (request.issueTime() > windowEnd) {
return false;
}
if (request.endTime !== -1 && request.endTime < windowStart) {
return false;
}
return true;
}
static copyRequestHeaders(request) {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(request.requestHeadersText());
}
static copyResponseHeaders(request) {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(request.responseHeadersText);
}
static async copyResponse(request) {
const contentData = await request.contentData();
let content = contentData.content || '';
if (!request.contentType().isTextType()) {
content = TextUtils.ContentProvider.contentAsDataURL(content, request.mimeType, contentData.encoded);
}
else if (contentData.encoded && content) {
content = window.atob(content);
}
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(content);
}
handleDrop(dataTransfer) {
const items = dataTransfer.items;
if (!items.length) {
return;
}
const file = items[0].getAsFile();
if (file) {
void this.onLoadFromFile(file);
}
}
async onLoadFromFile(file) {
const outputStream = new Common.StringOutputStream.StringOutputStream();
const reader = new Bindings.FileUtils.ChunkedFileReader(file, /* chunkSize */ 10000000);
const success = await reader.read(outputStream);
if (!success) {
const error = reader.error();
if (error) {
this.harLoadFailed(error.message);
}
return;
}
let harRoot;
try {
// HARRoot and JSON.parse might throw.
harRoot = new HAR.HARFormat.HARRoot(JSON.parse(outputStream.data()));
}
catch (e) {
this.harLoadFailed(e);
return;
}
Logs.NetworkLog.NetworkLog.instance().importRequests(HAR.Importer.Importer.requestsFromHARLog(harRoot.log));
}
harLoadFailed(message) {
Common.Console.Console.instance().error('Failed to load HAR file with following error: ' + message);
}
setGrouping(groupKey) {
if (this.activeGroupLookup) {
this.activeGroupLookup.reset();
}
const groupLookup = groupKey ? this.groupLookups.get(groupKey) || null : null;
this.activeGroupLookup = groupLookup;
this.invalidateAllItems();
}
computeRowHeight() {
return Math.round(this.rawRowHeight * window.devicePixelRatio) / window.devicePixelRatio;
}
nodeForRequest(request) {
return networkRequestToNode.get(request) || null;
}
headerHeight() {
return this.headerHeightInternal;
}
setRecording(recording) {
this.recording = recording;
this.updateSummaryBar();
}
columns() {
return this.columnsInternal;
}
summaryToolbar() {
return this.summaryToolbarInternal;
}
modelAdded(networkManager) {
// TODO(allada) Remove dependency on networkManager and instead use NetworkLog and PageLoad for needed data.
const target = networkManager.target();
if (target.outermostTarget() !== target) {
return;
}
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
if (resourceTreeModel) {
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this.loadEventFired, this);
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.DOMContentLoaded, this.domContentLoadedEventFired, this);
}
for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
if (this.isInScope(request)) {
this.refreshRequest(request);
}
}
}
modelRemoved(networkManager) {
const target = networkManager.target();
if (target.outermostTarget() !== target) {
return;
}
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
if (resourceTreeModel) {
resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.Load, this.loadEventFired, this);
resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.DOMContentLoaded, this.domContentLoadedEventFired, this);
}
const preserveLog = Common.Settings.Settings.instance().moduleSetting('network_log.preserve-log').get();
if (!preserveLog) {
this.reset();
}
}
linkifier() {
return this.linkifierInternal;
}
setWindow(start, end) {
if (!start && !end) {
this.timeFilter = null;
this.timeCalculatorInternal.setWindow(null);
}
else {
this.timeFilter = NetworkLogView.requestTimeFilter.bind(null, start, end);
this.timeCalculatorInternal.setWindow(new NetworkTimeBoundary(start, end));
}
this.filterRequests();
}
resetFocus() {
this.dataGrid.element.focus();
}
resetSuggestionBuilder() {
this.suggestionBuilder.clear();
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.Is, NetworkForward.UIFilter.IsFilterType.Running);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.Is, NetworkForward.UIFilter.IsFilterType.FromCache);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.Is, NetworkForward.UIFilter.IsFilterType.ServiceWorkerIntercepted);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.Is, NetworkForward.UIFilter.IsFilterType.ServiceWorkerInitiated);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.LargerThan, '100');
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.LargerThan, '10k');
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.LargerThan, '1M');
this.textFilterUI.setSuggestionProvider(this.suggestionBuilder.completions.bind(this.suggestionBuilder));
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.HasOverrides, overrideFilter.yes);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.HasOverrides, overrideFilter.no);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.HasOverrides, overrideFilter.content);
this.suggestionBuilder.addItem(NetworkForward.UIFilter.FilterType.HasOverrides, overrideFilter.headers);
}
filterChanged() {
this.removeAllNodeHighlights();
this.parseFilterQuery(this.textFilterUI.value(), this.invertFilterUI.checked());
this.filterRequests();
this.textFilterSetting.set(this.textFilterUI.value());
}
async resetFilter() {
this.textFilterUI.clear();
}
showRecordingHint() {
this.hideRecordingHint();
this.recordingHint = this.element.createChild('div', 'network-status-pane fill');
const hintText = this.recordingHint.createChild('div', 'recording-hint');
if (this.recording) {
let reloadShortcutNode = null;
const reloadShortcut = UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction('inspector_main.reload')[0];
if (reloadShortcut) {
reloadShortcutNode = this.recordingHint.createChild('b');
reloadShortcutNode.textContent = reloadShortcut.title();
}
const recordingText = hintText.createChild('span');
recordingText.textContent = i18nString(UIStrings.recordingNetworkActivity);
if (reloadShortcutNode) {
hintText.createChild('br');
hintText.appendChild(i18n.i18n.getFormatLocalizedString(str_, UIStrings.performARequestOrHitSToRecordThe, { PH1: reloadShortcutNode }));
}
}
else {
const recordNode = hintText.createChild('b');
recordNode.textContent =
UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutTitleForAction('network.toggle-recording') || '';
hintText.appendChild(i18n.i18n.getFormatLocalizedString(str_, UIStrings.recordToDisplayNetworkActivity, { PH1: recordNode }));
}
hintText.createChild('br');
hintText.appendChild(UI.XLink.XLink.create('https://developer.chrome.com/docs/devtools/network/?utm_source=devtools&utm_campaign=2019Q1', i18nString(UIStrings.learnMore)));
this.setHidden(true);
}
hideRecordingHint() {
this.setHidden(false);
if (this.recordingHint) {
this.recordingHint.remove();
}
UI.ARIAUtils.alert(i18nString(UIStrings.networkDataAvailable));
this.recordingHint = null;
}
setHidden(value) {
this.columnsInternal.setHidden(value);
UI.ARIAUtils.setHidden(this.summaryToolbarInternal.element, value);
}
elementsToRestoreScrollPositionsFor() {
if (!this.dataGrid) // Not initialized yet.
{
return [];
}
return [this.dataGrid.scrollContainer];
}
columnExtensionResolved() {
this.invalidateAllItems(true);
}
setupDataGrid() {
this.dataGrid.setRowContextMenuCallback((contextMenu, node) => {
const request = node.request();
if (request) {
this.handleContextMenuForRequest(contextMenu, request);
}
});
this.dataGrid.setStickToBottom(true);
this.dataGrid.setName('networkLog');
this.dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);
this.dataGrid.element.classList.add('network-log-grid');
this.dataGrid.element.addEventListener('mousedown', this.dataGridMouseDown.bind(this), true);
this.dataGrid.element.addEventListener('mousemove', this.dataGridMouseMove.bind(this), true);
this.dataGrid.element.addEventListener('mouseleave', () => this.setHoveredNode(null), true);
this.dataGrid.element.addEventListener('keydown', event => {
if (event.key === 'ArrowRight' && this.dataGrid.selectedNode) {
const initiatorLink = this.dataGrid.selectedNode.element().querySelector('span.devtools-link');
if (initiatorLink) {
initiatorLink.focus();
}
}
if (Platform.KeyboardUtilities.isEnterOrSpaceKey(event)) {
this.dispatchEventToListeners(Events.RequestActivated, { showPanel: true, takeFocus: true });
event.consume(true);
}
});
this.dataGrid.element.addEventListener('keyup', event => {
if ((event.key === 'r' || event.key === 'R') && this.dataGrid.selectedNode) {
const request = this.dataGrid.selectedNode.request();
if (!request) {
return;
}
if (SDK.NetworkManager.NetworkManager.canReplayRequest(request)) {
SDK.NetworkManager.NetworkManager.replayRequest(request);
}
}
});
this.dataGrid.element.addEventListener('focus', this.onDataGridFocus.bind(this), true);
this.dataGrid.element.addEventListener('blur', this.onDataGridBlur.bind(this), true);
return this.dataGrid;
}
dataGridMouseMove(event) {
const mouseEvent = event;
const node = (this.dataGrid.dataGridNodeFromNode(mouseEvent.target));
const highlightInitiatorChain = mouseEvent.shiftKey;
this.setHoveredNode(node, highlightInitiatorChain);
}
hoveredNode() {
return this.hoveredNodeInternal;
}
setHoveredNode(node, highlightInitiatorChain) {
if (this.hoveredNodeInternal) {
this.hoveredNodeInternal.setHovered(false, false);
}
this.hoveredNodeInternal = node;
if (this.hoveredNodeInternal) {
this.hoveredNodeInternal.setHovered(true, Boolean(highlightInitiatorChain));
}
}
dataGridMouseDown(event) {
const mouseEvent = event;
if (!this.dataGrid.selectedNode && mouseEvent.button) {
mouseEvent.consume();
}
}
updateSummaryBar() {
this.hideRecordingHint();
let transferSize = 0;
let resourceSize = 0;
let selectedNodeNumber = 0;
let selectedTransferSize = 0;
let selectedResourceSize = 0;
let baseTime = -1;
let maxTime = -1;
let nodeCount = 0;
for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
const node = networkRequestToNode.get(request);
if (!node) {
continue;
}
nodeCount++;
const requestTransferSize = request.transferSize;
transferSize += requestTransferSize;
const requestResourceSize = request.resourceSize;
resourceSize += requestResourceSize;
if (!filteredNetworkRequests.has(node)) {
selectedNodeNumber++;
selectedTransferSize += requestTransferSize;
selectedResourceSize += requestResourceSize;
}
const networkManager = SDK.NetworkManager.NetworkManager.forRequest(request);
// TODO(allada) inspectedURL should be stored in PageLoad used instead of target so HAR requests can have an
// inspected url.
if (networkManager && request.url() === networkManager.target().inspectedURL() &&
request.resourceType() === Common.ResourceType.resourceTypes.Document &&
networkManager.target().parentTarget()?.type() !== SDK.Target.Type.Frame) {
baseTime = request.startTime;
}
if (request.endTime > maxTime) {
maxTime = request.endTime;
}
}
if (!nodeCount) {
this.showRecordingHint();
return;
}
this.summaryToolbarInternal.removeToolbarItems();
const appendChunk = (chunk, title) => {
const toolbarText = new UI.Toolbar.ToolbarText(chunk);
toolbarText.setTitle(title ? title : chunk);
this.summaryToolbarInternal.appendToolbarItem(toolbarText);
return toolbarText.element;
};
if (selectedNodeNumber !== nodeCount) {
appendChunk(i18nString(UIStrings.sSRequests, { PH1: selectedNodeNumber, PH2: nodeCount }));
this.summaryToolbarInternal.appendSeparator();
appendChunk(i18nString(UIStrings.sSTransferred, {
PH1: Platform.NumberUtilities.bytesToString(selectedTransferSize),
PH2: Platform.NumberUtilities.bytesToString(transferSize),
}), i18nString(UIStrings.sBSBTransferredOverNetwork, { PH1: selectedTransferSize, PH2: transferSize }));
this.summaryToolbarInternal.appendSeparator();
appendChunk(i18nString(UIStrings.sSResources, {
PH1: Platform.NumberUtilities.bytesToString(selectedResourceSize),
PH2: Platform.NumberUtilities.bytesToString(resourceSize),
}), i18nString(UIStrings.sBSBResourcesLoadedByThePage, { PH1: selectedResourceSize, PH2: resourceSize }));
}
else {
appendChunk(i18nString(UIStrings.sRequests, { PH1: nodeCount }));
this.summaryToolbarInternal.appendSeparator();
appendChunk(i18nString(UIStrings.sTransferred, { PH1: Platform.NumberUtilities.bytesToString(transferSize) }), i18nString(UIStrings.sBTransferredOverNetwork, { PH1: transferSize }));
this.summaryToolbarInternal.appendSeparator();
appendChunk(i18nString(UIStrings.sResources, { PH1: Platform.NumberUtilities.bytesToString(resourceSize) }), i18nString(UIStrings.sBResourcesLoadedByThePage, { PH1: resourceSize }));
}
if (baseTime !== -1 && maxTime !== -1) {
this.summaryToolbarInternal.appendSeparator();
appendChunk(i18nString(UIStrings.finishS, { PH1: i18n.TimeUtilities.secondsToString(maxTime - baseTime) }));
if (this.mainRequestDOMContentLoadedTime !== -1 && this.mainRequestDOMContentLoadedTime > baseTime) {
this.summaryToolbarInternal.appendSeparator();
const domContentLoadedText = i18nString(UIStrings.domcontentloadedS, { PH1: i18n.TimeUtilities.secondsToString(this.mainRequestDOMContentLoadedTime - baseTime) });
appendChunk(domContentLoadedText).style.color = `var(${NetworkLogView.getDCLEventColor()})`;
}
if (this.mainRequestLoadTime !== -1) {
this.summaryToolbarInternal.appendSeparator();
const loadText = i18nString(UIStrings.loadS, { PH1: i18n.TimeUtilities.secondsToString(this.mainRequestLoadTime - baseTime) });
appendChunk(loadText).style.color = `var(${NetworkLogView.getLoadEventColor()})`;
}
}
}
scheduleRefresh() {
if (this.needsRefresh) {
return;
}
this.needsRefresh = true;
if (this.isShowing()) {
void coordinator.write('NetworkLogView.render', this.refresh.bind(this));
}
}
addFilmStripFrames(times) {
this.columnsInternal.addEventDividers(times, 'network-frame-divider');
}
selectFilmStripFrame(time) {
this.columnsInternal.selectFilmStripFrame(time);
}
clearFilmStripFrame() {
this.columnsInternal.clearFilmStripFrame();
}
refreshIfNeeded() {
if (this.needsRefresh) {
this.refresh();
}
}
invalidateAllItems(deferUpdate) {
this.staleRequests = ne