UNPKG

debug-server-next

Version:

Dev server for hippy-core.

1,094 lines 89.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. */ /* eslint-disable rulesdir/no_underscored_properties */ 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 SDK from '../../core/sdk/sdk.js'; import * as Bindings from '../../models/bindings/bindings.js'; import * as HAR from '../../models/har/har.js'; import * as IssuesManager from '../../models/issues_manager/issues_manager.js'; import * as Logs from '../../models/logs/logs.js'; import * as TextUtils from '../../models/text_utils/text_utils.js'; import * as NetworkForward from '../../panels/network/forward/forward.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 * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.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: 'Hides data: and blob: URLs', /** *@description Aria accessible name in Network Log View of the Network panel */ resourceTypesToInclude: 'Resource types to include', /** *@description Label for a filter in the Network panel */ hasBlockedCookies: 'Has blocked 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. */ onlyShowRequestsWithBlocked: 'Only show 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: 'Only show 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: 'Shows 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 */ recordSToDisplayNetworkActivity: 'Record ({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?', }; const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkLogView.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class NetworkLogView extends UI.Widget.VBox { _networkInvertFilterSetting; _networkHideDataURLSetting; _networkShowIssuesOnlySetting; _networkOnlyBlockedRequestsSetting; _networkOnlyThirdPartySetting; _networkResourceTypeFiltersSetting; _rawRowHeight; _progressBarContainer; _networkLogLargeRowsSetting; _rowHeight; _timeCalculator; _durationCalculator; _calculator; _columns; _staleRequests; _mainRequestLoadTime; _mainRequestDOMContentLoadedTime; _filters; _timeFilter; _hoveredNode; _recordingHint; _refreshRequestId; _highlightedNode; _linkifier; _recording; _needsRefresh; _headerHeight; _groupLookups; _activeGroupLookup; _textFilterUI; _invertFilterUI; _dataURLFilterUI; _resourceCategoryFilterUI; _onlyIssuesFilterUI; _onlyBlockedRequestsUI; _onlyThirdPartyFilterUI; _filterParser; _suggestionBuilder; _dataGrid; _summaryToolbar; _filterBar; _textFilterSetting; constructor(filterBar, progressBarContainer, networkLogLargeRowsSetting) { super(); this.setMinimumSize(50, 64); this.registerRequiredCSS('panels/network/networkLogView.css'); 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._networkShowIssuesOnlySetting = Common.Settings.Settings.instance().createSetting('networkShowIssuesOnly', 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._rowHeight = this._computeRowHeight(); } this._rawRowHeight = 0; this._rowHeight = 0; updateRowHeight.call(this); this._timeCalculator = new NetworkTransferTimeCalculator(); this._durationCalculator = new NetworkTransferDurationCalculator(); this._calculator = this._timeCalculator; this._columns = new NetworkLogViewColumns(this, this._timeCalculator, this._durationCalculator, networkLogLargeRowsSetting); this._columns.show(this.element); this._staleRequests = new Set(); this._mainRequestLoadTime = -1; this._mainRequestDOMContentLoadedTime = -1; this._filters = []; this._timeFilter = null; this._hoveredNode = null; this._recordingHint = null; this._refreshRequestId = null; this._highlightedNode = null; this._linkifier = new Components.Linkifier.Linkifier(); this._recording = false; this._needsRefresh = false; this._headerHeight = 0; this._groupLookups = new Map(); this._groupLookups.set('Frame', new NetworkFrameGrouper(this)); this._activeGroupLookup = null; this._textFilterUI = new UI.FilterBar.TextFilterUI(); this._textFilterUI.addEventListener(UI.FilterBar.FilterUI.Events.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(UI.FilterBar.FilterUI.Events.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(UI.FilterBar.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this); UI.Tooltip.Tooltip.install(this._dataURLFilterUI.element(), i18nString(UIStrings.hidesDataAndBlobUrls)); filterBar.addFilter(this._dataURLFilterUI); 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.setAccessibleName(this._resourceCategoryFilterUI.element(), i18nString(UIStrings.resourceTypesToInclude)); this._resourceCategoryFilterUI.addEventListener(UI.FilterBar.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this); filterBar.addFilter(this._resourceCategoryFilterUI); this._onlyIssuesFilterUI = new UI.FilterBar.CheckboxFilterUI('only-show-issues', i18nString(UIStrings.hasBlockedCookies), true, this._networkShowIssuesOnlySetting); this._onlyIssuesFilterUI.addEventListener(UI.FilterBar.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this); UI.Tooltip.Tooltip.install(this._onlyIssuesFilterUI.element(), i18nString(UIStrings.onlyShowRequestsWithBlocked)); filterBar.addFilter(this._onlyIssuesFilterUI); this._onlyBlockedRequestsUI = new UI.FilterBar.CheckboxFilterUI('only-show-blocked-requests', i18nString(UIStrings.blockedRequests), true, this._networkOnlyBlockedRequestsSetting); this._onlyBlockedRequestsUI.addEventListener(UI.FilterBar.FilterUI.Events.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(UI.FilterBar.FilterUI.Events.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._columns.dataGrid(); this._setupDataGrid(); this._columns.sortByCurrentColumn(); filterBar.filterButton().addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._dataGrid.scheduleUpdate.bind(this._dataGrid, true /* isFromUser */)); this._summaryToolbar = new UI.Toolbar.Toolbar('network-summary-bar', this.element); this._summaryToolbar.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); 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.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 _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" /* OptionallyBlockable */; } if (value === NetworkForward.UIFilter.MixedContentFilterValues.Blocked) { return request.mixedContentType === "blockable" /* Blockable */ && request.wasBlocked(); } if (value === NetworkForward.UIFilter.MixedContentFilterValues.BlockOverridden) { return request.mixedContentType === "blockable" /* Blockable */ && !request.wasBlocked(); } if (value === NetworkForward.UIFilter.MixedContentFilterValues.All) { return request.mixedContentType !== "none" /* 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 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 entry = items[0].webkitGetAsEntry(); if (entry.isDirectory) { return; } entry.file(this.onLoadFromFile.bind(this)); } 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._headerHeight; } setRecording(recording) { this._recording = recording; this._updateSummaryBar(); } modelAdded(networkManager) { // TODO(allada) Remove dependency on networkManager and instead use NetworkLog and PageLoad for needed data. if (networkManager.target().parentTarget()) { return; } const resourceTreeModel = networkManager.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); } } modelRemoved(networkManager) { if (!networkManager.target().parentTarget()) { const resourceTreeModel = networkManager.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); } } } linkifier() { return this._linkifier; } setWindow(start, end) { if (!start && !end) { this._timeFilter = null; this._timeCalculator.setWindow(null); } else { this._timeFilter = NetworkLogView._requestTimeFilter.bind(null, start, end); this._timeCalculator.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)); } _filterChanged(_event) { 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.recordSToDisplayNetworkActivity, { 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._columns.setHidden(value); UI.ARIAUtils.setHidden(this._summaryToolbar.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 (isEnterOrSpaceKey(event)) { this.dispatchEventToListeners(Events.RequestActivated, { showPanel: true, takeFocus: true }); event.consume(true); } }); 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._hoveredNode; } _setHoveredNode(node, highlightInitiatorChain) { if (this._hoveredNode) { this._hoveredNode.setHovered(false, false); } this._hoveredNode = node; if (this._hoveredNode) { this._hoveredNode.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()) { baseTime = request.startTime; } if (request.endTime > maxTime) { maxTime = request.endTime; } } if (!nodeCount) { this._showRecordingHint(); return; } this._summaryToolbar.removeToolbarItems(); const appendChunk = (chunk, title) => { const toolbarText = new UI.Toolbar.ToolbarText(chunk); toolbarText.setTitle(title ? title : chunk); this._summaryToolbar.appendToolbarItem(toolbarText); return toolbarText.element; }; if (selectedNodeNumber !== nodeCount) { appendChunk(i18nString(UIStrings.sSRequests, { PH1: selectedNodeNumber, PH2: nodeCount })); this._summaryToolbar.appendSeparator(); appendChunk(i18nString(UIStrings.sSTransferred, { PH1: Platform.NumberUtilities.bytesToString(selectedTransferSize), PH2: Platform.NumberUtilities.bytesToString(transferSize), }), i18nString(UIStrings.sBSBTransferredOverNetwork, { PH1: selectedTransferSize, PH2: transferSize })); this._summaryToolbar.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._summaryToolbar.appendSeparator(); appendChunk(i18nString(UIStrings.sTransferred, { PH1: Platform.NumberUtilities.bytesToString(transferSize) }), i18nString(UIStrings.sBTransferredOverNetwork, { PH1: transferSize })); this._summaryToolbar.appendSeparator(); appendChunk(i18nString(UIStrings.sResources, { PH1: Platform.NumberUtilities.bytesToString(resourceSize) }), i18nString(UIStrings.sBResourcesLoadedByThePage, { PH1: resourceSize })); } if (baseTime !== -1 && maxTime !== -1) { this._summaryToolbar.appendSeparator(); appendChunk(i18nString(UIStrings.finishS, { PH1: i18n.TimeUtilities.secondsToString(maxTime - baseTime) })); if (this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) { this._summaryToolbar.appendSeparator(); const domContentLoadedText = i18nString(UIStrings.domcontentloadedS, { PH1: i18n.TimeUtilities.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime) }); appendChunk(domContentLoadedText).style.color = NetworkLogView.getDCLEventColor(); } if (this._mainRequestLoadTime !== -1) { this._summaryToolbar.appendSeparator(); const loadText = i18nString(UIStrings.loadS, { PH1: i18n.TimeUtilities.secondsToString(this._mainRequestLoadTime - baseTime) }); appendChunk(loadText).style.color = NetworkLogView.getLoadEventColor(); } } } scheduleRefresh() { if (this._needsRefresh) { return; } this._needsRefresh = true; if (this.isShowing() && !this._refreshRequestId) { this._refreshRequestId = this.element.window().requestAnimationFrame(this._refresh.bind(this)); } } addFilmStripFrames(times) { this._columns.addEventDividers(times, 'network-frame-divider'); } selectFilmStripFrame(time) { this._columns.selectFilmStripFrame(time); } clearFilmStripFrame() { this._columns.clearFilmStripFrame(); } _refreshIfNeeded() { if (this._needsRefresh) { this._refresh(); } } _invalidateAllItems(deferUpdate) { this._staleRequests = new Set(Logs.NetworkLog.NetworkLog.instance().requests()); if (deferUpdate) { this.scheduleRefresh(); } else { this._refresh(); } } timeCalculator() { return this._timeCalculator; } calculator() { return this._calculator; } setCalculator(x) { if (!x || this._calculator === x) { return; } if (this._calculator !== x) { this._calculator = x; this._columns.setCalculator(this._calculator); } this._calculator.reset(); if (this._calculator.startAtZero) { this._columns.hideEventDividers(); } else { this._columns.showEventDividers(); } this._invalidateAllItems(); } _loadEventFired(event) { if (!this._recording) { return; } const time = event.data.loadTime; if (time) { this._mainRequestLoadTime = time; this._columns.addEventDividers([time], 'network-load-divider'); } } _domContentLoadedEventFired(event) { if (!this._recording) { return; } const data = event.data; if (data) { this._mainRequestDOMContentLoadedTime = data; this._columns.addEventDividers([data], 'network-dcl-divider'); } } wasShown() { this._refreshIfNeeded(); this._columns.wasShown(); } willHide() { this._columns.willHide(); } onResize() { this._rowHeight = this._computeRowHeight(); } flatNodesList() { const rootNode = this._dataGrid.rootNode(); return rootNode.flatChildren(); } _onDataGridFocus() { if (this._dataGrid.element.matches(':focus-visible')) { this.element.classList.add('grid-focused'); } this.updateNodeBackground(); } _onDataGridBlur() { this.element.classList.remove('grid-focused'); this.updateNodeBackground(); } updateNodeBackground() { if (this._dataGrid.selectedNode) { this._dataGrid.selectedNode.updateBackgroundColor(); } } updateNodeSelectedClass(isSelected) { if (isSelected) { this.element.classList.remove('no-node-selected'); } else { this.element.classList.add('no-node-selected'); } } stylesChanged() { this._columns.scheduleRefresh(); } _refresh() { this._needsRefresh = false; if (this._refreshRequestId) { this.element.window().cancelAnimationFrame(this._refreshRequestId); this._refreshRequestId = null; } this.removeAllNodeHighlights(); this._timeCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime); this._durationCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime); this._timeCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime); this._durationCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime); const nodesToInsert = new Map(); const nodesToRefresh = []; const staleNodes = new Set(); // While creating nodes it may add more entries into _staleRequests because redirect request nodes update the parent // node so we loop until we have no more stale requests. while (this._staleRequests.size) { const request = this._staleRequests.values().next().value; this._staleRequests.delete(request); let node = networkRequestToNode.get(request); if (!node) { node = this._createNodeForRequest(request); } staleNodes.add(node); } for (const node of staleNodes) { const isFilteredOut = !this._applyFilter(node); if (isFilteredOut && node === this._hoveredNode) { this._setHoveredNode(null); } if (!isFilteredOut) { nodesToRefresh.push(node); } const request = node.request(); this._timeCalculator.updateBoundaries(request); this._durationCalculator.updateBoundaries(request); const newParent = this._parentNodeForInsert(node); const wasAlreadyFiltered = filteredNetworkRequests.has(node); if (wasAlreadyFiltered === isFilteredOut && node.parent === newParent) { continue; } if (isFiltere