@dcloudio/uni-debugger
Version:
uni-app debugger
1,488 lines (1,295 loc) • 60.8 kB
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
*/
_hi