UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

1,039 lines 98.9 kB
// 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