mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 21:54:47 +08:00
We used to listen to resize events from the root DevTools window or a SplitWidget, but not when the viewport grows/shrinks due to toggling the filter bar. This CL updates the viewport in that case, too. Bug: 807917 Change-Id: I7c5cfbe20f7e293f808e8138b2c7da0ab76df845 Reviewed-on: https://chromium-review.googlesource.com/1077757 Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Commit-Queue: Erik Luo <luoe@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#563331} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 4dcc9eb7b3652120ef1b3bd07bdd9b918c9a7d60
1828 lines
59 KiB
JavaScript
1828 lines
59 KiB
JavaScript
/*
|
|
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
|
|
* Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
|
|
* Copyright (C) 2011 Google Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @implements {SDK.SDKModelObserver<!SDK.NetworkManager>}
|
|
*/
|
|
Network.NetworkLogView = class extends UI.VBox {
|
|
/**
|
|
* @param {!UI.FilterBar} filterBar
|
|
* @param {!Element} progressBarContainer
|
|
* @param {!Common.Setting} networkLogLargeRowsSetting
|
|
*/
|
|
constructor(filterBar, progressBarContainer, networkLogLargeRowsSetting) {
|
|
super();
|
|
this.setMinimumSize(50, 64);
|
|
this.registerRequiredCSS('network/networkLogView.css');
|
|
|
|
this.element.id = 'network-container';
|
|
|
|
this._networkHideDataURLSetting = Common.settings.createSetting('networkHideDataURL', false);
|
|
this._networkResourceTypeFiltersSetting = Common.settings.createSetting('networkResourceTypeFilters', {});
|
|
|
|
this._rawRowHeight = 0;
|
|
this._progressBarContainer = progressBarContainer;
|
|
this._networkLogLargeRowsSetting = networkLogLargeRowsSetting;
|
|
this._networkLogLargeRowsSetting.addChangeListener(updateRowHeight.bind(this), this);
|
|
|
|
/**
|
|
* @this {Network.NetworkLogView}
|
|
*/
|
|
function updateRowHeight() {
|
|
this._rawRowHeight = !!this._networkLogLargeRowsSetting.get() ? 41 : 21;
|
|
this._rowHeight = this._computeRowHeight();
|
|
}
|
|
this._rawRowHeight = 0;
|
|
this._rowHeight = 0;
|
|
updateRowHeight.call(this);
|
|
|
|
/** @type {!Network.NetworkTransferTimeCalculator} */
|
|
this._timeCalculator = new Network.NetworkTransferTimeCalculator();
|
|
/** @type {!Network.NetworkTransferDurationCalculator} */
|
|
this._durationCalculator = new Network.NetworkTransferDurationCalculator();
|
|
this._calculator = this._timeCalculator;
|
|
|
|
this._columns = new Network.NetworkLogViewColumns(
|
|
this, this._timeCalculator, this._durationCalculator, networkLogLargeRowsSetting);
|
|
this._columns.show(this.element);
|
|
|
|
/** @type {!Set<!SDK.NetworkRequest>} */
|
|
this._staleRequests = new Set();
|
|
/** @type {number} */
|
|
this._mainRequestLoadTime = -1;
|
|
/** @type {number} */
|
|
this._mainRequestDOMContentLoadedTime = -1;
|
|
this._highlightedSubstringChanges = [];
|
|
|
|
/** @type {!Array.<!Network.NetworkLogView.Filter>} */
|
|
this._filters = [];
|
|
/** @type {?Network.NetworkLogView.Filter} */
|
|
this._timeFilter = null;
|
|
/** @type {?Network.NetworkNode} */
|
|
this._hoveredNode = null;
|
|
/** @type {?Element} */
|
|
this._recordingHint = null;
|
|
/** @type {?number} */
|
|
this._refreshRequestId = null;
|
|
/** @type {?Network.NetworkRequestNode} */
|
|
this._highlightedNode = null;
|
|
|
|
this.linkifier = new Components.Linkifier();
|
|
this.badgePool = new ProductRegistry.BadgePool();
|
|
|
|
this._recording = false;
|
|
this._needsRefresh = false;
|
|
|
|
this._headerHeight = 0;
|
|
|
|
/** @type {!Map<string, !Network.GroupLookupInterface>} */
|
|
this._groupLookups = new Map();
|
|
this._groupLookups.set('Frame', new Network.NetworkFrameGrouper(this));
|
|
|
|
/** @type {?Network.GroupLookupInterface} */
|
|
this._activeGroupLookup = null;
|
|
|
|
this._textFilterUI = new UI.TextFilterUI();
|
|
this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this);
|
|
filterBar.addFilter(this._textFilterUI);
|
|
|
|
this._dataURLFilterUI = new UI.CheckboxFilterUI(
|
|
'hide-data-url', Common.UIString('Hide data URLs'), true, this._networkHideDataURLSetting);
|
|
this._dataURLFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
|
|
filterBar.addFilter(this._dataURLFilterUI);
|
|
|
|
const filterItems =
|
|
Object.values(Common.resourceCategories)
|
|
.map(category => ({name: category.title, label: category.shortTitle, title: category.title}));
|
|
this._resourceCategoryFilterUI = new UI.NamedBitSetFilterUI(filterItems, this._networkResourceTypeFiltersSetting);
|
|
this._resourceCategoryFilterUI.addEventListener(
|
|
UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
|
|
filterBar.addFilter(this._resourceCategoryFilterUI);
|
|
|
|
this._filterParser = new TextUtils.FilterParser(Network.NetworkLogView._searchKeys);
|
|
this._suggestionBuilder =
|
|
new UI.FilterSuggestionBuilder(Network.NetworkLogView._searchKeys, Network.NetworkLogView._sortSearchValues);
|
|
this._resetSuggestionBuilder();
|
|
|
|
this._dataGrid = this._columns.dataGrid();
|
|
this._setupDataGrid();
|
|
this._columns.sortByCurrentColumn();
|
|
filterBar.filterButton().addEventListener(
|
|
UI.ToolbarButton.Events.Click, this._dataGrid.scheduleUpdate.bind(this._dataGrid, true /* isFromUser */));
|
|
|
|
this._summaryBarElement = this.element.createChild('div', 'network-summary-bar');
|
|
|
|
new UI.DropTarget(
|
|
this.element, [UI.DropTarget.Type.File], Common.UIString('Drop HAR files here'), this._handleDrop.bind(this));
|
|
|
|
Common.moduleSetting('networkColorCodeResourceTypes')
|
|
.addChangeListener(this._invalidateAllItems.bind(this, false), this);
|
|
|
|
SDK.targetManager.observeModels(SDK.NetworkManager, this);
|
|
BrowserSDK.networkLog.addEventListener(BrowserSDK.NetworkLog.Events.RequestAdded, this._onRequestUpdated, this);
|
|
BrowserSDK.networkLog.addEventListener(BrowserSDK.NetworkLog.Events.RequestUpdated, this._onRequestUpdated, this);
|
|
BrowserSDK.networkLog.addEventListener(BrowserSDK.NetworkLog.Events.Reset, this._reset, this);
|
|
|
|
this._updateGroupByFrame();
|
|
Common.moduleSetting('network.group-by-frame').addChangeListener(() => this._updateGroupByFrame());
|
|
|
|
this._filterBar = filterBar;
|
|
}
|
|
|
|
_updateGroupByFrame() {
|
|
const value = Common.moduleSetting('network.group-by-frame').get();
|
|
this._setGrouping(value ? 'Frame' : null);
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @param {!Array<string>} values
|
|
*/
|
|
static _sortSearchValues(key, values) {
|
|
if (key === Network.NetworkLogView.FilterType.Priority) {
|
|
values.sort((a, b) => {
|
|
const aPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(a));
|
|
const bPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(b));
|
|
return PerfUI.networkPriorityWeight(aPriority) - PerfUI.networkPriorityWeight(bPriority);
|
|
});
|
|
} else {
|
|
values.sort();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkLogView.Filter} filter
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _negativeFilter(filter, request) {
|
|
return !filter(request);
|
|
}
|
|
|
|
/**
|
|
* @param {?RegExp} regex
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestPathFilter(regex, request) {
|
|
if (!regex)
|
|
return false;
|
|
|
|
return regex.test(request.path() + '/' + request.name());
|
|
}
|
|
|
|
/**
|
|
* @param {string} domain
|
|
* @return {!Array.<string>}
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @return {!Network.NetworkLogView.Filter}
|
|
*/
|
|
static _createRequestDomainFilter(value) {
|
|
/**
|
|
* @param {string} string
|
|
* @return {string}
|
|
*/
|
|
function escapeForRegExp(string) {
|
|
return string.escapeForRegExp();
|
|
}
|
|
const escapedPattern = value.split('*').map(escapeForRegExp).join('.*');
|
|
return Network.NetworkLogView._requestDomainFilter.bind(null, new RegExp('^' + escapedPattern + '$', 'i'));
|
|
}
|
|
|
|
/**
|
|
* @param {!RegExp} regex
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestDomainFilter(regex, request) {
|
|
return regex.test(request.domain);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _runningRequestFilter(request) {
|
|
return !request.finished;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _fromCacheRequestFilter(request) {
|
|
return request.cached();
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestResponseHeaderFilter(value, request) {
|
|
return request.responseHeaderValue(value) !== undefined;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestMethodFilter(value, request) {
|
|
return request.requestMethod === value;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestPriorityFilter(value, request) {
|
|
return request.priority() === value;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestMimeTypeFilter(value, request) {
|
|
return request.mimeType === value;
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkLogView.MixedContentFilterValues} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestMixedContentFilter(value, request) {
|
|
if (value === Network.NetworkLogView.MixedContentFilterValues.Displayed)
|
|
return request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable;
|
|
else if (value === Network.NetworkLogView.MixedContentFilterValues.Blocked)
|
|
return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && request.wasBlocked();
|
|
else if (value === Network.NetworkLogView.MixedContentFilterValues.BlockOverridden)
|
|
return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && !request.wasBlocked();
|
|
else if (value === Network.NetworkLogView.MixedContentFilterValues.All)
|
|
return request.mixedContentType !== Protocol.Security.MixedContentType.None;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestSchemeFilter(value, request) {
|
|
return request.scheme === value;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestSetCookieDomainFilter(value, request) {
|
|
const cookies = request.responseCookies;
|
|
for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
|
|
if (cookies[i].domain() === value)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestSetCookieNameFilter(value, request) {
|
|
const cookies = request.responseCookies;
|
|
for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
|
|
if (cookies[i].name() === value)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestSetCookieValueFilter(value, request) {
|
|
const cookies = request.responseCookies;
|
|
for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
|
|
if (cookies[i].value() === value)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {number} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestSizeLargerThanFilter(value, request) {
|
|
return request.transferSize >= value;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _statusCodeFilter(value, request) {
|
|
return ('' + request.statusCode) === value;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static HTTPRequestsFilter(request) {
|
|
return request.parsedURL.isValid && (request.scheme in Network.NetworkLogView.HTTPSchemas);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static FinishedRequestsFilter(request) {
|
|
return request.finished;
|
|
}
|
|
|
|
/**
|
|
* @param {number} windowStart
|
|
* @param {number} windowEnd
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {boolean}
|
|
*/
|
|
static _requestTimeFilter(windowStart, windowEnd, request) {
|
|
if (request.issueTime() > windowEnd)
|
|
return false;
|
|
if (request.endTime !== -1 && request.endTime < windowStart)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
static _copyRequestHeaders(request) {
|
|
InspectorFrontendHost.copyText(request.requestHeadersText());
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
static _copyResponseHeaders(request) {
|
|
InspectorFrontendHost.copyText(request.responseHeadersText);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
static async _copyResponse(request) {
|
|
const contentData = await request.contentData();
|
|
let content = contentData.content;
|
|
if (contentData.encoded) {
|
|
content = Common.ContentProvider.contentAsDataURL(
|
|
contentData.content, request.mimeType, contentData.encoded, contentData.encoded ? 'utf-8' : null);
|
|
}
|
|
InspectorFrontendHost.copyText(content || '');
|
|
}
|
|
|
|
/**
|
|
* @param {!DataTransfer} dataTransfer
|
|
*/
|
|
_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));
|
|
}
|
|
|
|
/**
|
|
* @param {!File} file
|
|
*/
|
|
async _onLoadFromFile(file) {
|
|
const outputStream = new Common.StringOutputStream();
|
|
const reader = new Bindings.ChunkedFileReader(file, /* chunkSize */ 10000000);
|
|
const success = await reader.read(outputStream);
|
|
if (!success) {
|
|
this._harLoadFailed(reader.error().message);
|
|
return;
|
|
}
|
|
let harRoot;
|
|
try {
|
|
// HARRoot and JSON.parse might throw.
|
|
harRoot = new HARImporter.HARRoot(JSON.parse(outputStream.data()));
|
|
} catch (e) {
|
|
this._harLoadFailed(e);
|
|
return;
|
|
}
|
|
BrowserSDK.networkLog.importRequests(HARImporter.Importer.requestsFromHARLog(harRoot.log));
|
|
}
|
|
|
|
/**
|
|
* @param {string} message
|
|
*/
|
|
_harLoadFailed(message) {
|
|
Common.console.error('Failed to load HAR file with following error: ' + message);
|
|
}
|
|
|
|
/**
|
|
* @param {?string} groupKey
|
|
*/
|
|
_setGrouping(groupKey) {
|
|
if (this._activeGroupLookup)
|
|
this._activeGroupLookup.reset();
|
|
const groupLookup = groupKey ? this._groupLookups.get(groupKey) || null : null;
|
|
this._activeGroupLookup = groupLookup;
|
|
this._invalidateAllItems();
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
_computeRowHeight() {
|
|
return Math.round(this._rawRowHeight * window.devicePixelRatio) / window.devicePixelRatio;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {?Network.NetworkRequestNode}
|
|
*/
|
|
nodeForRequest(request) {
|
|
return request[Network.NetworkLogView._networkNodeSymbol] || null;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
headerHeight() {
|
|
return this._headerHeight;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} recording
|
|
*/
|
|
setRecording(recording) {
|
|
this._recording = recording;
|
|
this._updateSummaryBar();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.NetworkManager} networkManager
|
|
*/
|
|
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);
|
|
if (resourceTreeModel) {
|
|
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
|
|
resourceTreeModel.addEventListener(
|
|
SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.NetworkManager} networkManager
|
|
*/
|
|
modelRemoved(networkManager) {
|
|
if (!networkManager.target().parentTarget()) {
|
|
const resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
|
|
if (resourceTreeModel) {
|
|
resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
|
|
resourceTreeModel.removeEventListener(
|
|
SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} start
|
|
* @param {number} end
|
|
*/
|
|
setWindow(start, end) {
|
|
if (!start && !end) {
|
|
this._timeFilter = null;
|
|
this._timeCalculator.setWindow(null);
|
|
} else {
|
|
this._timeFilter = Network.NetworkLogView._requestTimeFilter.bind(null, start, end);
|
|
this._timeCalculator.setWindow(new Network.NetworkTimeBoundary(start, end));
|
|
}
|
|
this._filterRequests();
|
|
}
|
|
|
|
clearSelection() {
|
|
if (this._dataGrid.selectedNode)
|
|
this._dataGrid.selectedNode.deselect();
|
|
}
|
|
|
|
_resetSuggestionBuilder() {
|
|
this._suggestionBuilder.clear();
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.Running);
|
|
this._suggestionBuilder.addItem(
|
|
Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.FromCache);
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '100');
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '10k');
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '1M');
|
|
this._textFilterUI.setSuggestionProvider(this._suggestionBuilder.completions.bind(this._suggestionBuilder));
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_filterChanged(event) {
|
|
this.removeAllNodeHighlights();
|
|
this._parseFilterQuery(this._textFilterUI.value());
|
|
this._filterRequests();
|
|
}
|
|
|
|
_showRecordingHint() {
|
|
this._hideRecordingHint();
|
|
this._recordingHint = this.element.createChild('div', 'network-status-pane fill');
|
|
const hintText = this._recordingHint.createChild('div', 'recording-hint');
|
|
const reloadShortcutNode = this._recordingHint.createChild('b');
|
|
reloadShortcutNode.textContent = UI.shortcutRegistry.shortcutDescriptorsForAction('inspector_main.reload')[0].name;
|
|
|
|
if (this._recording) {
|
|
const recordingText = hintText.createChild('span');
|
|
recordingText.textContent = Common.UIString('Recording network activity\u2026');
|
|
hintText.createChild('br');
|
|
hintText.appendChild(
|
|
UI.formatLocalized('Perform a request or hit %s to record the reload.', [reloadShortcutNode]));
|
|
} else {
|
|
const recordNode = hintText.createChild('b');
|
|
recordNode.textContent = UI.shortcutRegistry.shortcutTitleForAction('network.toggle-recording');
|
|
hintText.appendChild(UI.formatLocalized(
|
|
'Record (%s) or reload (%s) to display network activity.', [recordNode, reloadShortcutNode]));
|
|
}
|
|
}
|
|
|
|
_hideRecordingHint() {
|
|
if (this._recordingHint)
|
|
this._recordingHint.remove();
|
|
this._recordingHint = null;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Array.<!Element>}
|
|
*/
|
|
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);
|
|
return this._dataGrid;
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_dataGridMouseMove(event) {
|
|
const node = (this._dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.target)));
|
|
const highlightInitiatorChain = event.shiftKey;
|
|
this._setHoveredNode(node, highlightInitiatorChain);
|
|
}
|
|
|
|
/**
|
|
* @return {?Network.NetworkNode}
|
|
*/
|
|
hoveredNode() {
|
|
return this._hoveredNode;
|
|
}
|
|
|
|
/**
|
|
* @param {?Network.NetworkNode} node
|
|
* @param {boolean=} highlightInitiatorChain
|
|
*/
|
|
_setHoveredNode(node, highlightInitiatorChain) {
|
|
if (this._hoveredNode)
|
|
this._hoveredNode.setHovered(false, false);
|
|
this._hoveredNode = node;
|
|
if (this._hoveredNode)
|
|
this._hoveredNode.setHovered(true, !!highlightInitiatorChain);
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_dataGridMouseDown(event) {
|
|
if (!this._dataGrid.selectedNode && event.button)
|
|
event.consume();
|
|
}
|
|
|
|
_updateSummaryBar() {
|
|
this._hideRecordingHint();
|
|
|
|
let transferSize = 0;
|
|
let selectedNodeNumber = 0;
|
|
let selectedTransferSize = 0;
|
|
let baseTime = -1;
|
|
let maxTime = -1;
|
|
|
|
let nodeCount = 0;
|
|
for (const request of BrowserSDK.networkLog.requests()) {
|
|
const node = request[Network.NetworkLogView._networkNodeSymbol];
|
|
if (!node)
|
|
continue;
|
|
nodeCount++;
|
|
const requestTransferSize = request.transferSize;
|
|
transferSize += requestTransferSize;
|
|
if (!node[Network.NetworkLogView._isFilteredOutSymbol]) {
|
|
selectedNodeNumber++;
|
|
selectedTransferSize += requestTransferSize;
|
|
}
|
|
const networkManager = SDK.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.resourceTypes.Document && !networkManager.target().parentTarget())
|
|
baseTime = request.startTime;
|
|
if (request.endTime > maxTime)
|
|
maxTime = request.endTime;
|
|
}
|
|
|
|
if (!nodeCount) {
|
|
this._showRecordingHint();
|
|
return;
|
|
}
|
|
|
|
const summaryBar = this._summaryBarElement;
|
|
summaryBar.removeChildren();
|
|
const separator = '\u2002\u2758\u2002';
|
|
let text = '';
|
|
/**
|
|
* @param {string} chunk
|
|
* @return {!Element}
|
|
*/
|
|
function appendChunk(chunk) {
|
|
const span = summaryBar.createChild('span');
|
|
span.textContent = chunk;
|
|
text += chunk;
|
|
return span;
|
|
}
|
|
|
|
if (selectedNodeNumber !== nodeCount) {
|
|
appendChunk(Common.UIString('%d / %d requests', selectedNodeNumber, nodeCount));
|
|
appendChunk(separator);
|
|
appendChunk(Common.UIString(
|
|
'%s / %s transferred', Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize)));
|
|
} else {
|
|
appendChunk(Common.UIString('%d requests', nodeCount));
|
|
appendChunk(separator);
|
|
appendChunk(Common.UIString('%s transferred', Number.bytesToString(transferSize)));
|
|
}
|
|
if (baseTime !== -1 && maxTime !== -1) {
|
|
appendChunk(separator);
|
|
appendChunk(Common.UIString('Finish: %s', Number.secondsToString(maxTime - baseTime)));
|
|
if (this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
|
|
appendChunk(separator);
|
|
const domContentLoadedText = Common.UIString(
|
|
'DOMContentLoaded: %s', Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
|
|
appendChunk(domContentLoadedText).classList.add('summary-blue');
|
|
}
|
|
if (this._mainRequestLoadTime !== -1) {
|
|
appendChunk(separator);
|
|
const loadText = Common.UIString('Load: %s', Number.secondsToString(this._mainRequestLoadTime - baseTime));
|
|
appendChunk(loadText).classList.add('summary-red');
|
|
}
|
|
}
|
|
summaryBar.title = text;
|
|
}
|
|
|
|
scheduleRefresh() {
|
|
if (this._needsRefresh)
|
|
return;
|
|
|
|
this._needsRefresh = true;
|
|
|
|
if (this.isShowing() && !this._refreshRequestId)
|
|
this._refreshRequestId = this.element.window().requestAnimationFrame(this._refresh.bind(this));
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<number>} times
|
|
*/
|
|
addFilmStripFrames(times) {
|
|
this._columns.addEventDividers(times, 'network-frame-divider');
|
|
}
|
|
|
|
/**
|
|
* @param {number} time
|
|
*/
|
|
selectFilmStripFrame(time) {
|
|
this._columns.selectFilmStripFrame(time);
|
|
}
|
|
|
|
clearFilmStripFrame() {
|
|
this._columns.clearFilmStripFrame();
|
|
}
|
|
|
|
_refreshIfNeeded() {
|
|
if (this._needsRefresh)
|
|
this._refresh();
|
|
}
|
|
|
|
/**
|
|
* @param {boolean=} deferUpdate
|
|
*/
|
|
_invalidateAllItems(deferUpdate) {
|
|
this._staleRequests = new Set(BrowserSDK.networkLog.requests());
|
|
if (deferUpdate)
|
|
this.scheduleRefresh();
|
|
else
|
|
this._refresh();
|
|
}
|
|
|
|
/**
|
|
* @return {!Network.NetworkTimeCalculator}
|
|
*/
|
|
timeCalculator() {
|
|
return this._timeCalculator;
|
|
}
|
|
|
|
/**
|
|
* @return {!Network.NetworkTimeCalculator}
|
|
*/
|
|
calculator() {
|
|
return this._calculator;
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkTimeCalculator} x
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_loadEventFired(event) {
|
|
if (!this._recording)
|
|
return;
|
|
|
|
const time = /** @type {number} */ (event.data.loadTime);
|
|
if (time) {
|
|
this._mainRequestLoadTime = time;
|
|
this._columns.addEventDividers([time], 'network-red-divider');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_domContentLoadedEventFired(event) {
|
|
if (!this._recording)
|
|
return;
|
|
const data = /** @type {number} */ (event.data);
|
|
if (data) {
|
|
this._mainRequestDOMContentLoadedTime = data;
|
|
this._columns.addEventDividers([data], 'network-blue-divider');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
wasShown() {
|
|
this._refreshIfNeeded();
|
|
this._columns.wasShown();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
willHide() {
|
|
this._columns.willHide();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
onResize() {
|
|
this._rowHeight = this._computeRowHeight();
|
|
}
|
|
|
|
/**
|
|
* @return {!Array<!Network.NetworkNode>}
|
|
*/
|
|
flatNodesList() {
|
|
return this._dataGrid.rootNode().flatChildren();
|
|
}
|
|
|
|
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);
|
|
|
|
/** @type {!Map<!Network.NetworkNode, !Network.NetworkNode>} */
|
|
const nodesToInsert = new Map();
|
|
/** @type {!Array<!Network.NetworkNode>} */
|
|
const nodesToRefresh = [];
|
|
|
|
/** @type {!Set<!Network.NetworkRequestNode>} */
|
|
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.firstValue();
|
|
this._staleRequests.delete(request);
|
|
let node = request[Network.NetworkLogView._networkNodeSymbol];
|
|
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);
|
|
if (node[Network.NetworkLogView._isFilteredOutSymbol] === isFilteredOut && node.parent === newParent)
|
|
continue;
|
|
node[Network.NetworkLogView._isFilteredOutSymbol] = isFilteredOut;
|
|
const removeFromParent = node.parent && (isFilteredOut || node.parent !== newParent);
|
|
if (removeFromParent) {
|
|
let parent = node.parent;
|
|
parent.removeChild(node);
|
|
while (parent && !parent.hasChildren() && parent.dataGrid && parent.dataGrid.rootNode() !== parent) {
|
|
const grandparent = parent.parent;
|
|
grandparent.removeChild(parent);
|
|
parent = grandparent;
|
|
}
|
|
}
|
|
|
|
if (!newParent || isFilteredOut)
|
|
continue;
|
|
|
|
if (!newParent.dataGrid && !nodesToInsert.has(newParent)) {
|
|
nodesToInsert.set(newParent, this._dataGrid.rootNode());
|
|
nodesToRefresh.push(newParent);
|
|
}
|
|
nodesToInsert.set(node, newParent);
|
|
}
|
|
|
|
for (const node of nodesToInsert.keys())
|
|
nodesToInsert.get(node).appendChild(node);
|
|
|
|
for (const node of nodesToRefresh)
|
|
node.refresh();
|
|
|
|
this._updateSummaryBar();
|
|
|
|
if (nodesToInsert.size)
|
|
this._columns.sortByCurrentColumn();
|
|
|
|
this._dataGrid.updateInstantly();
|
|
this._didRefreshForTest();
|
|
}
|
|
|
|
_didRefreshForTest() {
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkRequestNode} node
|
|
* @return {?Network.NetworkNode}
|
|
*/
|
|
_parentNodeForInsert(node) {
|
|
if (!this._activeGroupLookup)
|
|
return this._dataGrid.rootNode();
|
|
|
|
const groupNode = this._activeGroupLookup.groupNodeForRequest(node.request());
|
|
if (!groupNode)
|
|
return this._dataGrid.rootNode();
|
|
return groupNode;
|
|
}
|
|
|
|
_reset() {
|
|
this.dispatchEventToListeners(Network.NetworkLogView.Events.RequestSelected, null);
|
|
|
|
this._setHoveredNode(null);
|
|
this._columns.reset();
|
|
|
|
this._timeFilter = null;
|
|
this._calculator.reset();
|
|
|
|
this._timeCalculator.setWindow(null);
|
|
this.linkifier.reset();
|
|
this.badgePool.reset();
|
|
|
|
if (this._activeGroupLookup)
|
|
this._activeGroupLookup.reset();
|
|
this._staleRequests.clear();
|
|
this._resetSuggestionBuilder();
|
|
|
|
this._mainRequestLoadTime = -1;
|
|
this._mainRequestDOMContentLoadedTime = -1;
|
|
|
|
this._dataGrid.rootNode().removeChildren();
|
|
this._updateSummaryBar();
|
|
this._dataGrid.setStickToBottom(true);
|
|
this.scheduleRefresh();
|
|
}
|
|
|
|
/**
|
|
* @param {string} filterString
|
|
*/
|
|
setTextFilterValue(filterString) {
|
|
this._textFilterUI.setValue(filterString);
|
|
this._dataURLFilterUI.setChecked(false);
|
|
this._resourceCategoryFilterUI.reset();
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
_createNodeForRequest(request) {
|
|
const node = new Network.NetworkRequestNode(this, request);
|
|
request[Network.NetworkLogView._networkNodeSymbol] = node;
|
|
node[Network.NetworkLogView._isFilteredOutSymbol] = true;
|
|
|
|
for (let redirect = request.redirectSource(); redirect; redirect = redirect.redirectSource())
|
|
this._refreshRequest(redirect);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_onRequestUpdated(event) {
|
|
const request = /** @type {!SDK.NetworkRequest} */ (event.data);
|
|
this._refreshRequest(request);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
_refreshRequest(request) {
|
|
Network.NetworkLogView._subdomains(request.domain)
|
|
.forEach(
|
|
this._suggestionBuilder.addItem.bind(this._suggestionBuilder, Network.NetworkLogView.FilterType.Domain));
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Method, request.requestMethod);
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MimeType, request.mimeType);
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Scheme, '' + request.scheme);
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.StatusCode, '' + request.statusCode);
|
|
|
|
const priority = request.priority();
|
|
if (priority) {
|
|
this._suggestionBuilder.addItem(
|
|
Network.NetworkLogView.FilterType.Priority, PerfUI.uiLabelForNetworkPriority(priority));
|
|
}
|
|
|
|
if (request.mixedContentType !== Protocol.Security.MixedContentType.None) {
|
|
this._suggestionBuilder.addItem(
|
|
Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.All);
|
|
}
|
|
|
|
if (request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable) {
|
|
this._suggestionBuilder.addItem(
|
|
Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.Displayed);
|
|
}
|
|
|
|
if (request.mixedContentType === Protocol.Security.MixedContentType.Blockable) {
|
|
const suggestion = request.wasBlocked() ? Network.NetworkLogView.MixedContentFilterValues.Blocked :
|
|
Network.NetworkLogView.MixedContentFilterValues.BlockOverridden;
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MixedContent, suggestion);
|
|
}
|
|
|
|
const responseHeaders = request.responseHeaders;
|
|
for (let i = 0, l = responseHeaders.length; i < l; ++i)
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
|
|
const cookies = request.responseCookies;
|
|
for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
|
|
const cookie = cookies[i];
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieName, cookie.name());
|
|
this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieValue, cookie.value());
|
|
}
|
|
|
|
this._staleRequests.add(request);
|
|
this.scheduleRefresh();
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
rowHeight() {
|
|
return this._rowHeight;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} gridMode
|
|
*/
|
|
switchViewMode(gridMode) {
|
|
this._columns.switchViewMode(gridMode);
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.ContextMenu} contextMenu
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
handleContextMenuForRequest(contextMenu, request) {
|
|
contextMenu.appendApplicableItems(request);
|
|
let copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
|
|
const footerSection = copyMenu.footerSection();
|
|
if (request) {
|
|
copyMenu.defaultSection().appendItem(
|
|
UI.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, request.contentURL()));
|
|
if (request.requestHeadersText()) {
|
|
copyMenu.defaultSection().appendItem(
|
|
Common.UIString('Copy request headers'), Network.NetworkLogView._copyRequestHeaders.bind(null, request));
|
|
}
|
|
|
|
if (request.responseHeadersText) {
|
|
copyMenu.defaultSection().appendItem(
|
|
Common.UIString('Copy response headers'), Network.NetworkLogView._copyResponseHeaders.bind(null, request));
|
|
}
|
|
|
|
if (request.finished) {
|
|
copyMenu.defaultSection().appendItem(
|
|
Common.UIString('Copy response'), Network.NetworkLogView._copyResponse.bind(null, request));
|
|
}
|
|
|
|
if (Host.isWin()) {
|
|
footerSection.appendItem(
|
|
Common.UIString('Copy as PowerShell'), this._copyPowerShellCommand.bind(this, request));
|
|
footerSection.appendItem(Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request));
|
|
footerSection.appendItem(
|
|
Common.UIString('Copy as cURL (cmd)'), this._copyCurlCommand.bind(this, request, 'win'));
|
|
footerSection.appendItem(
|
|
Common.UIString('Copy as cURL (bash)'), this._copyCurlCommand.bind(this, request, 'unix'));
|
|
footerSection.appendItem(Common.UIString('Copy all as PowerShell'), this._copyAllPowerShellCommand.bind(this));
|
|
footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
|
|
footerSection.appendItem(Common.UIString('Copy all as cURL (cmd)'), this._copyAllCurlCommand.bind(this, 'win'));
|
|
footerSection.appendItem(
|
|
Common.UIString('Copy all as cURL (bash)'), this._copyAllCurlCommand.bind(this, 'unix'));
|
|
} else {
|
|
footerSection.appendItem(Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request));
|
|
footerSection.appendItem(Common.UIString('Copy as cURL'), this._copyCurlCommand.bind(this, request, 'unix'));
|
|
footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
|
|
footerSection.appendItem(Common.UIString('Copy all as cURL'), this._copyAllCurlCommand.bind(this, 'unix'));
|
|
}
|
|
} else {
|
|
copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
|
|
}
|
|
footerSection.appendItem(Common.UIString('Copy all as HAR'), this._copyAll.bind(this));
|
|
|
|
contextMenu.saveSection().appendItem(Common.UIString('Save as HAR with content'), this._exportAll.bind(this));
|
|
|
|
contextMenu.editSection().appendItem(Common.UIString('Clear browser cache'), this._clearBrowserCache.bind(this));
|
|
contextMenu.editSection().appendItem(
|
|
Common.UIString('Clear browser cookies'), this._clearBrowserCookies.bind(this));
|
|
|
|
if (request) {
|
|
const maxBlockedURLLength = 20;
|
|
const manager = SDK.multitargetNetworkManager;
|
|
let patterns = manager.blockedPatterns();
|
|
|
|
const urlWithoutScheme = request.parsedURL.urlWithoutScheme();
|
|
if (urlWithoutScheme && !patterns.find(pattern => pattern.url === urlWithoutScheme)) {
|
|
contextMenu.debugSection().appendItem(
|
|
Common.UIString('Block request URL'), addBlockedURL.bind(null, urlWithoutScheme));
|
|
} else if (urlWithoutScheme) {
|
|
const croppedURL = urlWithoutScheme.trimMiddle(maxBlockedURLLength);
|
|
contextMenu.debugSection().appendItem(
|
|
Common.UIString('Unblock %s', croppedURL), removeBlockedURL.bind(null, urlWithoutScheme));
|
|
}
|
|
|
|
const domain = request.parsedURL.domain();
|
|
if (domain && !patterns.find(pattern => pattern.url === domain)) {
|
|
contextMenu.debugSection().appendItem(
|
|
Common.UIString('Block request domain'), addBlockedURL.bind(null, domain));
|
|
} else if (domain) {
|
|
const croppedDomain = domain.trimMiddle(maxBlockedURLLength);
|
|
contextMenu.debugSection().appendItem(
|
|
Common.UIString('Unblock %s', croppedDomain), removeBlockedURL.bind(null, domain));
|
|
}
|
|
|
|
if (SDK.NetworkManager.canReplayRequest(request)) {
|
|
contextMenu.debugSection().appendItem(
|
|
Common.UIString('Replay XHR'), SDK.NetworkManager.replayRequest.bind(null, request));
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
*/
|
|
function addBlockedURL(url) {
|
|
patterns.push({enabled: true, url: url});
|
|
manager.setBlockedPatterns(patterns);
|
|
manager.setBlockingEnabled(true);
|
|
UI.viewManager.showView('network.blocked-urls');
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
*/
|
|
function removeBlockedURL(url) {
|
|
patterns = patterns.filter(pattern => pattern.url !== url);
|
|
manager.setBlockedPatterns(patterns);
|
|
UI.viewManager.showView('network.blocked-urls');
|
|
}
|
|
}
|
|
}
|
|
|
|
_harRequests() {
|
|
const httpRequests = BrowserSDK.networkLog.requests().filter(Network.NetworkLogView.HTTPRequestsFilter);
|
|
return httpRequests.filter(Network.NetworkLogView.FinishedRequestsFilter);
|
|
}
|
|
|
|
async _copyAll() {
|
|
const harArchive = {log: await BrowserSDK.HARLog.build(this._harRequests())};
|
|
InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @param {string} platform
|
|
*/
|
|
async _copyCurlCommand(request, platform) {
|
|
const command = await this._generateCurlCommand(request, platform);
|
|
InspectorFrontendHost.copyText(command);
|
|
}
|
|
|
|
/**
|
|
* @param {string} platform
|
|
*/
|
|
async _copyAllCurlCommand(platform) {
|
|
const requests = BrowserSDK.networkLog.requests();
|
|
const commands = await Promise.all(requests.map(request => this._generateCurlCommand(request, platform)));
|
|
if (platform === 'win')
|
|
InspectorFrontendHost.copyText(commands.join(' &\r\n'));
|
|
else
|
|
InspectorFrontendHost.copyText(commands.join(' ;\n'));
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @param {string} platform
|
|
*/
|
|
async _copyFetchCall(request, platform) {
|
|
const command = await this._generateFetchCall(request);
|
|
InspectorFrontendHost.copyText(command);
|
|
}
|
|
|
|
async _copyAllFetchCall() {
|
|
const requests = BrowserSDK.networkLog.requests();
|
|
const commands = await Promise.all(requests.map(request => this._generateFetchCall(request)));
|
|
InspectorFrontendHost.copyText(commands.join(' ;\n'));
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
async _copyPowerShellCommand(request) {
|
|
const command = await this._generatePowerShellCommand(request);
|
|
InspectorFrontendHost.copyText(command);
|
|
}
|
|
|
|
async _copyAllPowerShellCommand() {
|
|
const requests = BrowserSDK.networkLog.requests();
|
|
const commands = await Promise.all(requests.map(request => this._generatePowerShellCommand(request)));
|
|
InspectorFrontendHost.copyText(commands.join(';\r\n'));
|
|
}
|
|
|
|
async _exportAll() {
|
|
const url = SDK.targetManager.mainTarget().inspectedURL();
|
|
const parsedURL = url.asParsedURL();
|
|
const filename = parsedURL ? parsedURL.host : 'network-log';
|
|
const stream = new Bindings.FileOutputStream();
|
|
|
|
if (!await stream.open(filename + '.har'))
|
|
return;
|
|
|
|
const progressIndicator = new UI.ProgressIndicator();
|
|
this._progressBarContainer.appendChild(progressIndicator.element);
|
|
await Network.HARWriter.write(stream, this._harRequests(), progressIndicator);
|
|
progressIndicator.done();
|
|
stream.close();
|
|
}
|
|
|
|
_clearBrowserCache() {
|
|
if (confirm(Common.UIString('Are you sure you want to clear browser cache?')))
|
|
SDK.multitargetNetworkManager.clearBrowserCache();
|
|
}
|
|
|
|
_clearBrowserCookies() {
|
|
if (confirm(Common.UIString('Are you sure you want to clear browser cookies?')))
|
|
SDK.multitargetNetworkManager.clearBrowserCookies();
|
|
}
|
|
|
|
_removeAllHighlights() {
|
|
this.removeAllNodeHighlights();
|
|
for (let i = 0; i < this._highlightedSubstringChanges.length; ++i)
|
|
UI.revertDomChanges(this._highlightedSubstringChanges[i]);
|
|
this._highlightedSubstringChanges = [];
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkRequestNode} node
|
|
* @return {boolean}
|
|
*/
|
|
_applyFilter(node) {
|
|
const request = node.request();
|
|
if (this._timeFilter && !this._timeFilter(request))
|
|
return false;
|
|
const categoryName = request.resourceType().category().title;
|
|
if (!this._resourceCategoryFilterUI.accept(categoryName))
|
|
return false;
|
|
if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
|
|
return false;
|
|
if (request.statusText === 'Service Worker Fallback Required')
|
|
return false;
|
|
for (let i = 0; i < this._filters.length; ++i) {
|
|
if (!this._filters[i](request))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param {string} query
|
|
*/
|
|
_parseFilterQuery(query) {
|
|
const descriptors = this._filterParser.parse(query);
|
|
this._filters = descriptors.map(descriptor => {
|
|
const key = descriptor.key;
|
|
const text = descriptor.text || '';
|
|
const regex = descriptor.regex;
|
|
let filter;
|
|
if (key) {
|
|
const defaultText = (key + ':' + text).escapeForRegExp();
|
|
filter = this._createSpecialFilter(/** @type {!Network.NetworkLogView.FilterType} */ (key), text) ||
|
|
Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(defaultText, 'i'));
|
|
} else if (descriptor.regex) {
|
|
filter = Network.NetworkLogView._requestPathFilter.bind(null, /** @type {!RegExp} */ (regex));
|
|
} else {
|
|
filter = Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(text.escapeForRegExp(), 'i'));
|
|
}
|
|
return descriptor.negative ? Network.NetworkLogView._negativeFilter.bind(null, filter) : filter;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkLogView.FilterType} type
|
|
* @param {string} value
|
|
* @return {?Network.NetworkLogView.Filter}
|
|
*/
|
|
_createSpecialFilter(type, value) {
|
|
switch (type) {
|
|
case Network.NetworkLogView.FilterType.Domain:
|
|
return Network.NetworkLogView._createRequestDomainFilter(value);
|
|
|
|
case Network.NetworkLogView.FilterType.HasResponseHeader:
|
|
return Network.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.Is:
|
|
if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.Running)
|
|
return Network.NetworkLogView._runningRequestFilter;
|
|
if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.FromCache)
|
|
return Network.NetworkLogView._fromCacheRequestFilter;
|
|
break;
|
|
|
|
case Network.NetworkLogView.FilterType.LargerThan:
|
|
return this._createSizeFilter(value.toLowerCase());
|
|
|
|
case Network.NetworkLogView.FilterType.Method:
|
|
return Network.NetworkLogView._requestMethodFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.MimeType:
|
|
return Network.NetworkLogView._requestMimeTypeFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.MixedContent:
|
|
return Network.NetworkLogView._requestMixedContentFilter.bind(
|
|
null, /** @type {!Network.NetworkLogView.MixedContentFilterValues} */ (value));
|
|
|
|
case Network.NetworkLogView.FilterType.Scheme:
|
|
return Network.NetworkLogView._requestSchemeFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.SetCookieDomain:
|
|
return Network.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.SetCookieName:
|
|
return Network.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.SetCookieValue:
|
|
return Network.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
|
|
|
|
case Network.NetworkLogView.FilterType.Priority:
|
|
return Network.NetworkLogView._requestPriorityFilter.bind(null, PerfUI.uiLabelToNetworkPriority(value));
|
|
|
|
case Network.NetworkLogView.FilterType.StatusCode:
|
|
return Network.NetworkLogView._statusCodeFilter.bind(null, value);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @return {?Network.NetworkLogView.Filter}
|
|
*/
|
|
_createSizeFilter(value) {
|
|
let multiplier = 1;
|
|
if (value.endsWith('k')) {
|
|
multiplier = 1024;
|
|
value = value.substring(0, value.length - 1);
|
|
} else if (value.endsWith('m')) {
|
|
multiplier = 1024 * 1024;
|
|
value = value.substring(0, value.length - 1);
|
|
}
|
|
const quantity = Number(value);
|
|
if (isNaN(quantity))
|
|
return null;
|
|
return Network.NetworkLogView._requestSizeLargerThanFilter.bind(null, quantity * multiplier);
|
|
}
|
|
|
|
_filterRequests() {
|
|
this._removeAllHighlights();
|
|
this._invalidateAllItems();
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {?Network.NetworkRequestNode}
|
|
*/
|
|
_reveal(request) {
|
|
this.removeAllNodeHighlights();
|
|
const node = request[Network.NetworkLogView._networkNodeSymbol];
|
|
if (!node || !node.dataGrid)
|
|
return null;
|
|
node.reveal();
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
revealAndHighlightRequest(request) {
|
|
const node = this._reveal(request);
|
|
if (node)
|
|
this._highlightNode(node);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
*/
|
|
selectRequest(request) {
|
|
this.setTextFilterValue('');
|
|
const node = this._reveal(request);
|
|
if (node)
|
|
node.select();
|
|
}
|
|
|
|
removeAllNodeHighlights() {
|
|
if (this._highlightedNode) {
|
|
this._highlightedNode.element().classList.remove('highlighted-row');
|
|
this._highlightedNode = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Network.NetworkRequestNode} node
|
|
*/
|
|
_highlightNode(node) {
|
|
UI.runCSSAnimationOnce(node.element(), 'highlighted-row');
|
|
this._highlightedNode = node;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {!Promise<string>}
|
|
*/
|
|
async _generateFetchCall(request) {
|
|
const ignoredHeaders = {
|
|
// Internal headers
|
|
'method': 1,
|
|
'path': 1,
|
|
'scheme': 1,
|
|
'version': 1,
|
|
|
|
// Unsafe headers
|
|
// Keep this list synchronized with src/net/http/http_util.cc
|
|
'accept-charset': 1,
|
|
'accept-encoding': 1,
|
|
'access-control-request-headers': 1,
|
|
'access-control-request-method': 1,
|
|
'connection': 1,
|
|
'content-length': 1,
|
|
'cookie': 1,
|
|
'cookie2': 1,
|
|
'date': 1,
|
|
'dnt': 1,
|
|
'expect': 1,
|
|
'host': 1,
|
|
'keep-alive': 1,
|
|
'origin': 1,
|
|
'referer': 1,
|
|
'te': 1,
|
|
'trailer': 1,
|
|
'transfer-encoding': 1,
|
|
'upgrade': 1,
|
|
'via': 1,
|
|
// TODO(phistuck) - remove this once crbug.com/571722 is fixed.
|
|
'user-agent': 1
|
|
};
|
|
|
|
const credentialHeaders = {'cookie': 1, 'authorization': 1};
|
|
|
|
const url = JSON.stringify(request.url());
|
|
|
|
const requestHeaders = request.requestHeaders();
|
|
const headerData = requestHeaders.reduce((result, header) => {
|
|
const name = header.name;
|
|
|
|
if (!ignoredHeaders[name.toLowerCase()] && !name.includes(':'))
|
|
result.append(name, header.value);
|
|
|
|
return result;
|
|
}, new Headers());
|
|
|
|
const headers = {};
|
|
for (const headerArray of headerData)
|
|
headers[headerArray[0]] = headers[headerArray[1]];
|
|
|
|
|
|
const credentials =
|
|
request.requestCookies || requestHeaders.some(({name}) => credentialHeaders[name.toLowerCase()]) ? 'include' :
|
|
'omit';
|
|
|
|
const referrerHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'referer');
|
|
|
|
const referrer = referrerHeader ? referrerHeader.value : void 0;
|
|
|
|
const referrerPolicy = request.referrerPolicy() || void 0;
|
|
|
|
const requestBody = await request.requestFormData();
|
|
|
|
const fetchOptions = {
|
|
credentials,
|
|
headers,
|
|
referrer,
|
|
referrerPolicy,
|
|
body: requestBody,
|
|
method: request.requestMethod,
|
|
mode: 'cors'
|
|
};
|
|
|
|
const options = JSON.stringify(fetchOptions);
|
|
return `fetch(${url}, ${options});`;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @param {string} platform
|
|
* @return {!Promise<string>}
|
|
*/
|
|
async _generateCurlCommand(request, platform) {
|
|
let command = ['curl'];
|
|
// These headers are derived from URL (except "version") and would be added by cURL anyway.
|
|
const ignoredHeaders = {'host': 1, 'method': 1, 'path': 1, 'scheme': 1, 'version': 1};
|
|
|
|
function escapeStringWin(str) {
|
|
/* If there are no new line characters do not escape the " characters
|
|
since it only uglifies the command.
|
|
|
|
Because cmd.exe parser and MS Crt arguments parsers use some of the
|
|
same escape characters, they can interact with each other in
|
|
horrible ways, the order of operations is critical.
|
|
|
|
Replace \ with \\ first because it is an escape character for certain
|
|
conditions in both parsers.
|
|
|
|
Replace all " with \" to ensure the first parser does not remove it.
|
|
|
|
Then escape all characters we are not sure about with ^ to ensure it
|
|
gets to MS Crt parser safely.
|
|
|
|
The % character is special because MS Crt parser will try and look for
|
|
ENV variables and fill them in it's place. We cannot escape them with %
|
|
and cannot escape them with ^ (because it's cmd.exe's escape not MS Crt
|
|
parser); So we can get cmd.exe parser to escape the character after it,
|
|
if it is followed by a valid beginning character of an ENV variable.
|
|
This ensures we do not try and double escape another ^ if it was placed
|
|
by the previous replace.
|
|
|
|
Lastly we replace new lines with ^ and TWO new lines because the first
|
|
new line is there to enact the escape command the second is the character
|
|
to escape (in this case new line).
|
|
*/
|
|
const encapsChars = /[\r\n]/.test(str) ? '^"' : '"';
|
|
return encapsChars +
|
|
str.replace(/\\/g, '\\\\')
|
|
.replace(/"/g, '\\"')
|
|
.replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
|
|
.replace(/%(?=[a-zA-Z0-9_])/g, '%^')
|
|
.replace(/\r\n|[\n\r]/g, '^\n\n') +
|
|
encapsChars;
|
|
}
|
|
|
|
/**
|
|
* @param {string} str
|
|
* @return {string}
|
|
*/
|
|
function escapeStringPosix(str) {
|
|
/**
|
|
* @param {string} x
|
|
* @return {string}
|
|
*/
|
|
function escapeCharacter(x) {
|
|
let code = x.charCodeAt(0);
|
|
if (code < 256) {
|
|
// Add leading zero when needed to not care about the next character.
|
|
return code < 16 ? '\\x0' + code.toString(16) : '\\x' + code.toString(16);
|
|
}
|
|
code = code.toString(16);
|
|
return '\\u' + ('0000' + code).substr(code.length, 4);
|
|
}
|
|
|
|
if (/[^\x20-\x7E]|\'/.test(str)) {
|
|
// Use ANSI-C quoting syntax.
|
|
return '$\'' +
|
|
str.replace(/\\/g, '\\\\')
|
|
.replace(/\'/g, '\\\'')
|
|
.replace(/\n/g, '\\n')
|
|
.replace(/\r/g, '\\r')
|
|
.replace(/[^\x20-\x7E]/g, escapeCharacter) +
|
|
'\'';
|
|
} else {
|
|
// Use single quote syntax.
|
|
return '\'' + str + '\'';
|
|
}
|
|
}
|
|
|
|
// cURL command expected to run on the same platform that DevTools run
|
|
// (it may be different from the inspected page platform).
|
|
const escapeString = platform === 'win' ? escapeStringWin : escapeStringPosix;
|
|
|
|
command.push(escapeString(request.url()).replace(/[[{}\]]/g, '\\$&'));
|
|
|
|
let inferredMethod = 'GET';
|
|
const data = [];
|
|
const requestContentType = request.requestContentType();
|
|
const formData = await request.requestFormData();
|
|
if (requestContentType && requestContentType.startsWith('application/x-www-form-urlencoded') && formData) {
|
|
data.push('--data');
|
|
data.push(escapeString(formData));
|
|
ignoredHeaders['content-length'] = true;
|
|
inferredMethod = 'POST';
|
|
} else if (formData) {
|
|
data.push('--data-binary');
|
|
data.push(escapeString(formData));
|
|
ignoredHeaders['content-length'] = true;
|
|
inferredMethod = 'POST';
|
|
}
|
|
|
|
if (request.requestMethod !== inferredMethod) {
|
|
command.push('-X');
|
|
command.push(request.requestMethod);
|
|
}
|
|
|
|
const requestHeaders = request.requestHeaders();
|
|
for (let i = 0; i < requestHeaders.length; i++) {
|
|
const header = requestHeaders[i];
|
|
const name = header.name.replace(/^:/, ''); // Translate SPDY v3 headers to HTTP headers.
|
|
if (name.toLowerCase() in ignoredHeaders)
|
|
continue;
|
|
command.push('-H');
|
|
command.push(escapeString(name + ': ' + header.value));
|
|
}
|
|
command = command.concat(data);
|
|
command.push('--compressed');
|
|
|
|
if (request.securityState() === Protocol.Security.SecurityState.Insecure)
|
|
command.push('--insecure');
|
|
return command.join(' ');
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {!Promise<string>}
|
|
*/
|
|
async _generatePowerShellCommand(request) {
|
|
const command = ['Invoke-WebRequest'];
|
|
const ignoredHeaders =
|
|
new Set(['host', 'connection', 'proxy-connection', 'content-length', 'expect', 'range', 'content-type']);
|
|
|
|
/**
|
|
* @param {string} str
|
|
* @return {string}
|
|
*/
|
|
function escapeString(str) {
|
|
return '"' +
|
|
str.replace(/[`\$"]/g, '`$&').replace(/[^\x20-\x7E]/g, char => '$([char]' + char.charCodeAt(0) + ')') + '"';
|
|
}
|
|
|
|
command.push('-Uri');
|
|
command.push(escapeString(request.url()));
|
|
|
|
if (request.requestMethod !== 'GET') {
|
|
command.push('-Method');
|
|
command.push(escapeString(request.requestMethod));
|
|
}
|
|
|
|
const requestHeaders = request.requestHeaders();
|
|
const headerNameValuePairs = [];
|
|
for (const header of requestHeaders) {
|
|
const name = header.name.replace(/^:/, ''); // Translate h2 headers to HTTP headers.
|
|
if (ignoredHeaders.has(name.toLowerCase()))
|
|
continue;
|
|
headerNameValuePairs.push(escapeString(name) + '=' + escapeString(header.value));
|
|
}
|
|
if (headerNameValuePairs.length) {
|
|
command.push('-Headers');
|
|
command.push('@{' + headerNameValuePairs.join('; ') + '}');
|
|
}
|
|
|
|
const contentTypeHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'content-type');
|
|
if (contentTypeHeader) {
|
|
command.push('-ContentType');
|
|
command.push(escapeString(contentTypeHeader.value));
|
|
}
|
|
|
|
const formData = await request.requestFormData();
|
|
if (formData) {
|
|
command.push('-Body');
|
|
const body = escapeString(formData);
|
|
if (/[^\x20-\x7E]/.test(formData))
|
|
command.push('([System.Text.Encoding]::UTF8.GetBytes(' + body + '))');
|
|
else
|
|
command.push(body);
|
|
}
|
|
|
|
return command.join(' ');
|
|
}
|
|
};
|
|
|
|
Network.NetworkLogView._isFilteredOutSymbol = Symbol('isFilteredOut');
|
|
Network.NetworkLogView._networkNodeSymbol = Symbol('NetworkNode');
|
|
|
|
Network.NetworkLogView.HTTPSchemas = {
|
|
'http': true,
|
|
'https': true,
|
|
'ws': true,
|
|
'wss': true
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
Network.NetworkLogView.Events = {
|
|
RequestSelected: Symbol('RequestSelected')
|
|
};
|
|
|
|
/** @enum {string} */
|
|
Network.NetworkLogView.FilterType = {
|
|
Domain: 'domain',
|
|
HasResponseHeader: 'has-response-header',
|
|
Is: 'is',
|
|
LargerThan: 'larger-than',
|
|
Method: 'method',
|
|
MimeType: 'mime-type',
|
|
MixedContent: 'mixed-content',
|
|
Priority: 'priority',
|
|
Scheme: 'scheme',
|
|
SetCookieDomain: 'set-cookie-domain',
|
|
SetCookieName: 'set-cookie-name',
|
|
SetCookieValue: 'set-cookie-value',
|
|
StatusCode: 'status-code'
|
|
};
|
|
|
|
/** @enum {string} */
|
|
Network.NetworkLogView.MixedContentFilterValues = {
|
|
All: 'all',
|
|
Displayed: 'displayed',
|
|
Blocked: 'blocked',
|
|
BlockOverridden: 'block-overridden'
|
|
};
|
|
|
|
/** @enum {string} */
|
|
Network.NetworkLogView.IsFilterType = {
|
|
Running: 'running',
|
|
FromCache: 'from-cache'
|
|
};
|
|
|
|
/** @type {!Array<string>} */
|
|
Network.NetworkLogView._searchKeys =
|
|
Object.keys(Network.NetworkLogView.FilterType).map(key => Network.NetworkLogView.FilterType[key]);
|
|
|
|
/** @typedef {function(!SDK.NetworkRequest): boolean} */
|
|
Network.NetworkLogView.Filter;
|
|
|
|
/**
|
|
* @interface
|
|
*/
|
|
Network.GroupLookupInterface = function() {};
|
|
|
|
Network.GroupLookupInterface.prototype = {
|
|
/**
|
|
* @param {!SDK.NetworkRequest} request
|
|
* @return {?Network.NetworkGroupNode}
|
|
*/
|
|
groupNodeForRequest: function(request) {},
|
|
|
|
reset: function() {}
|
|
};
|