blob: 19bc05754ef6818b7be6fd647dd5cb224f002ac5 [file] [log] [blame]
/*
* 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() {}
};