UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

1,488 lines (1,295 loc) 60.8 kB
/* * 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