| // Copyright 2018 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. | |
| /** | |
| * @implements {Search.SearchScope} | |
| */ | |
| export default class NetworkSearchScope { | |
| /** | |
| * @override | |
| * @param {!Common.Progress} progress | |
| */ | |
| performIndexing(progress) { | |
| setImmediate(progress.done.bind(progress)); | |
| } | |
| /** | |
| * @override | |
| * @param {!Search.SearchConfig} searchConfig | |
| * @param {!Common.Progress} progress | |
| * @param {function(!Search.SearchResult)} searchResultCallback | |
| * @param {function(boolean)} searchFinishedCallback | |
| * @return {?} | |
| */ | |
| async performSearch(searchConfig, progress, searchResultCallback, searchFinishedCallback) { | |
| const promises = []; | |
| const requests = SDK.networkLog.requests().filter(request => searchConfig.filePathMatchesFileQuery(request.url())); | |
| progress.setTotalWork(requests.length); | |
| for (const request of requests) { | |
| const promise = this._searchRequest(searchConfig, request, progress); | |
| promises.push(promise); | |
| } | |
| const results = await Promise.all(promises); | |
| if (progress.isCanceled()) { | |
| searchFinishedCallback(false); | |
| return; | |
| } | |
| for (const result of results.sort((r1, r2) => r1.label().localeCompare(r2.label()))) { | |
| if (result.matchesCount() > 0) { | |
| searchResultCallback(result); | |
| } | |
| } | |
| progress.done(); | |
| searchFinishedCallback(true); | |
| } | |
| /** | |
| * @param {!Search.SearchConfig} searchConfig | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {!Common.Progress} progress | |
| * @return {!Promise<?NetworkSearchResult>} | |
| */ | |
| async _searchRequest(searchConfig, request, progress) { | |
| let bodyMatches = []; | |
| if (request.contentType().isTextType()) { | |
| bodyMatches = | |
| await request.searchInContent(searchConfig.query(), !searchConfig.ignoreCase(), searchConfig.isRegex()); | |
| } | |
| if (progress.isCanceled()) { | |
| return null; | |
| } | |
| const locations = []; | |
| if (stringMatchesQuery(request.url())) { | |
| locations.push(UIRequestLocation.urlMatch(request)); | |
| } | |
| for (const header of request.requestHeaders()) { | |
| if (headerMatchesQuery(header)) { | |
| locations.push(UIRequestLocation.requestHeaderMatch(request, header)); | |
| } | |
| } | |
| for (const header of request.responseHeaders) { | |
| if (headerMatchesQuery(header)) { | |
| locations.push(UIRequestLocation.responseHeaderMatch(request, header)); | |
| } | |
| } | |
| for (const match of bodyMatches) { | |
| locations.push(UIRequestLocation.bodyMatch(request, match)); | |
| } | |
| progress.worked(); | |
| return new NetworkSearchResult(request, locations); | |
| /** | |
| * @param {!SDK.NetworkRequest.NameValue} header | |
| * @return {boolean} | |
| */ | |
| function headerMatchesQuery(header) { | |
| return stringMatchesQuery(`${header.name}: ${header.value}`); | |
| } | |
| /** | |
| * @param {string} string | |
| * @return {boolean} | |
| */ | |
| function stringMatchesQuery(string) { | |
| const flags = searchConfig.ignoreCase() ? 'i' : ''; | |
| const regExps = searchConfig.queries().map(query => new RegExp(query, flags)); | |
| let pos = 0; | |
| for (const regExp of regExps) { | |
| const match = string.substr(pos).match(regExp); | |
| if (!match) { | |
| return false; | |
| } | |
| pos += match.index + match[0].length; | |
| } | |
| return true; | |
| } | |
| } | |
| /** | |
| * @override | |
| */ | |
| stopSearch() { | |
| } | |
| } | |
| export class UIRequestLocation { | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {?SDK.NetworkRequest.NameValue} requestHeader | |
| * @param {?SDK.NetworkRequest.NameValue} responseHeader | |
| * @param {?Common.ContentProvider.SearchMatch} searchMatch | |
| * @param {boolean} urlMatch | |
| */ | |
| constructor(request, requestHeader, responseHeader, searchMatch, urlMatch) { | |
| this.request = request; | |
| this.requestHeader = requestHeader; | |
| this.responseHeader = responseHeader; | |
| this.searchMatch = searchMatch; | |
| this.isUrlMatch = urlMatch; | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {?SDK.NetworkRequest.NameValue} header | |
| */ | |
| static requestHeaderMatch(request, header) { | |
| return new UIRequestLocation(request, header, null, null, false); | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {?SDK.NetworkRequest.NameValue} header | |
| */ | |
| static responseHeaderMatch(request, header) { | |
| return new UIRequestLocation(request, null, header, null, false); | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {?Common.ContentProvider.SearchMatch} searchMatch | |
| */ | |
| static bodyMatch(request, searchMatch) { | |
| return new UIRequestLocation(request, null, null, searchMatch, false); | |
| } | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| */ | |
| static urlMatch(request) { | |
| return new UIRequestLocation(request, null, null, null, true); | |
| } | |
| } | |
| /** | |
| * @implements Search.SearchResult | |
| */ | |
| export class NetworkSearchResult { | |
| /** | |
| * @param {!SDK.NetworkRequest} request | |
| * @param {!Array<!UIRequestLocation>} locations | |
| */ | |
| constructor(request, locations) { | |
| this._request = request; | |
| this._locations = locations; | |
| } | |
| /** | |
| * @override | |
| * @return {number} | |
| */ | |
| matchesCount() { | |
| return this._locations.length; | |
| } | |
| /** | |
| * @override | |
| * @return {string} | |
| */ | |
| label() { | |
| return this._request.displayName; | |
| } | |
| /** | |
| * @override | |
| * @return {string} | |
| */ | |
| description() { | |
| const parsedUrl = this._request.parsedURL; | |
| if (!parsedUrl) { | |
| return this._request.url(); | |
| } | |
| return parsedUrl.urlWithoutScheme(); | |
| } | |
| /** | |
| * @override | |
| * @param {number} index | |
| * @return {string} | |
| */ | |
| matchLineContent(index) { | |
| const location = this._locations[index]; | |
| if (location.isUrlMatch) { | |
| return this._request.url(); | |
| } | |
| const header = location.requestHeader || location.responseHeader; | |
| if (header) { | |
| return header.value; | |
| } | |
| return location.searchMatch.lineContent; | |
| } | |
| /** | |
| * @override | |
| * @param {number} index | |
| * @return {!Object} | |
| */ | |
| matchRevealable(index) { | |
| return this._locations[index]; | |
| } | |
| /** | |
| * @override | |
| * @param {number} index | |
| * @return {?} | |
| */ | |
| matchLabel(index) { | |
| const location = this._locations[index]; | |
| if (location.isUrlMatch) { | |
| return Common.UIString('URL'); | |
| } | |
| const header = location.requestHeader || location.responseHeader; | |
| if (header) { | |
| return `${header.name}:`; | |
| } | |
| return location.searchMatch.lineNumber + 1; | |
| } | |
| } | |
| /* Legacy exported object */ | |
| self.Network = self.Network || {}; | |
| /* Legacy exported object */ | |
| Network = Network || {}; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkSearchScope = NetworkSearchScope; | |
| /** | |
| * @constructor | |
| */ | |
| Network.UIRequestLocation = UIRequestLocation; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkSearchResult = NetworkSearchResult; |