blob: b1475ae123a7aaea3fd24faa8efdfcf91713fb64 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <[email protected]>
4 * Copyright (C) 2011 Google Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @implements {SDK.SDKModelObserver<!SDK.NetworkManager>}
33 */
34Network.NetworkLogView = class extends UI.VBox {
35 /**
36 * @param {!UI.FilterBar} filterBar
37 * @param {!Element} progressBarContainer
38 * @param {!Common.Setting} networkLogLargeRowsSetting
39 */
40 constructor(filterBar, progressBarContainer, networkLogLargeRowsSetting) {
41 super();
42 this.setMinimumSize(50, 64);
43 this.registerRequiredCSS('network/networkLogView.css');
44
45 this.element.id = 'network-container';
46
47 this._networkHideDataURLSetting = Common.settings.createSetting('networkHideDataURL', false);
48 this._networkResourceTypeFiltersSetting = Common.settings.createSetting('networkResourceTypeFilters', {});
49
50 this._rawRowHeight = 0;
51 this._progressBarContainer = progressBarContainer;
52 this._networkLogLargeRowsSetting = networkLogLargeRowsSetting;
53 this._networkLogLargeRowsSetting.addChangeListener(updateRowHeight.bind(this), this);
54
55 /**
56 * @this {Network.NetworkLogView}
57 */
58 function updateRowHeight() {
59 this._rawRowHeight = !!this._networkLogLargeRowsSetting.get() ? 41 : 21;
60 this._rowHeight = this._computeRowHeight();
61 }
62 this._rawRowHeight = 0;
63 this._rowHeight = 0;
64 updateRowHeight.call(this);
65
66 /** @type {!Network.NetworkTransferTimeCalculator} */
67 this._timeCalculator = new Network.NetworkTransferTimeCalculator();
68 /** @type {!Network.NetworkTransferDurationCalculator} */
69 this._durationCalculator = new Network.NetworkTransferDurationCalculator();
70 this._calculator = this._timeCalculator;
71
72 this._columns = new Network.NetworkLogViewColumns(
73 this, this._timeCalculator, this._durationCalculator, networkLogLargeRowsSetting);
74 this._columns.show(this.element);
75
76 /** @type {!Set<!SDK.NetworkRequest>} */
77 this._staleRequests = new Set();
78 /** @type {number} */
79 this._mainRequestLoadTime = -1;
80 /** @type {number} */
81 this._mainRequestDOMContentLoadedTime = -1;
82 this._highlightedSubstringChanges = [];
83
84 /** @type {!Array.<!Network.NetworkLogView.Filter>} */
85 this._filters = [];
86 /** @type {?Network.NetworkLogView.Filter} */
87 this._timeFilter = null;
88 /** @type {?Network.NetworkNode} */
89 this._hoveredNode = null;
90 /** @type {?Element} */
91 this._recordingHint = null;
92 /** @type {?number} */
93 this._refreshRequestId = null;
94 /** @type {?Network.NetworkRequestNode} */
95 this._highlightedNode = null;
96
97 this.linkifier = new Components.Linkifier();
98 this.badgePool = new ProductRegistry.BadgePool();
99
100 this._recording = false;
101 this._needsRefresh = false;
102
103 this._headerHeight = 0;
104
105 /** @type {!Map<string, !Network.GroupLookupInterface>} */
106 this._groupLookups = new Map();
107 this._groupLookups.set('Frame', new Network.NetworkFrameGrouper(this));
108
109 /** @type {?Network.GroupLookupInterface} */
110 this._activeGroupLookup = null;
111
112 this._textFilterUI = new UI.TextFilterUI();
113 this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this);
114 filterBar.addFilter(this._textFilterUI);
115
116 this._dataURLFilterUI = new UI.CheckboxFilterUI(
117 'hide-data-url', Common.UIString('Hide data URLs'), true, this._networkHideDataURLSetting);
118 this._dataURLFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
Joey Arharba99d622019-02-01 19:10:48119 this._dataURLFilterUI.element().title = ls`Hides data: and blob: URLs`;
Blink Reformat4c46d092018-04-07 15:32:37120 filterBar.addFilter(this._dataURLFilterUI);
121
122 const filterItems =
123 Object.values(Common.resourceCategories)
124 .map(category => ({name: category.title, label: category.shortTitle, title: category.title}));
125 this._resourceCategoryFilterUI = new UI.NamedBitSetFilterUI(filterItems, this._networkResourceTypeFiltersSetting);
126 this._resourceCategoryFilterUI.addEventListener(
127 UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
128 filterBar.addFilter(this._resourceCategoryFilterUI);
129
130 this._filterParser = new TextUtils.FilterParser(Network.NetworkLogView._searchKeys);
131 this._suggestionBuilder =
132 new UI.FilterSuggestionBuilder(Network.NetworkLogView._searchKeys, Network.NetworkLogView._sortSearchValues);
133 this._resetSuggestionBuilder();
134
135 this._dataGrid = this._columns.dataGrid();
136 this._setupDataGrid();
137 this._columns.sortByCurrentColumn();
Erik Luo0187a022018-05-31 18:35:49138 filterBar.filterButton().addEventListener(
139 UI.ToolbarButton.Events.Click, this._dataGrid.scheduleUpdate.bind(this._dataGrid, true /* isFromUser */));
Blink Reformat4c46d092018-04-07 15:32:37140
Joey Arhara86c14e2019-03-12 03:20:50141 this._summaryToolbar = new UI.Toolbar('network-summary-bar', this.element);
Blink Reformat4c46d092018-04-07 15:32:37142
143 new UI.DropTarget(
144 this.element, [UI.DropTarget.Type.File], Common.UIString('Drop HAR files here'), this._handleDrop.bind(this));
145
146 Common.moduleSetting('networkColorCodeResourceTypes')
147 .addChangeListener(this._invalidateAllItems.bind(this, false), this);
148
149 SDK.targetManager.observeModels(SDK.NetworkManager, this);
Pavel Feldman18d13562018-07-31 03:31:18150 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestAdded, this._onRequestUpdated, this);
151 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestUpdated, this._onRequestUpdated, this);
152 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.Reset, this._reset, this);
Blink Reformat4c46d092018-04-07 15:32:37153
154 this._updateGroupByFrame();
155 Common.moduleSetting('network.group-by-frame').addChangeListener(() => this._updateGroupByFrame());
156
157 this._filterBar = filterBar;
Blink Reformat4c46d092018-04-07 15:32:37158 }
159
Blink Reformat4c46d092018-04-07 15:32:37160 _updateGroupByFrame() {
161 const value = Common.moduleSetting('network.group-by-frame').get();
162 this._setGrouping(value ? 'Frame' : null);
163 }
164
165 /**
166 * @param {string} key
167 * @param {!Array<string>} values
168 */
169 static _sortSearchValues(key, values) {
170 if (key === Network.NetworkLogView.FilterType.Priority) {
171 values.sort((a, b) => {
172 const aPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(a));
173 const bPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(b));
174 return PerfUI.networkPriorityWeight(aPriority) - PerfUI.networkPriorityWeight(bPriority);
175 });
176 } else {
177 values.sort();
178 }
179 }
180
181 /**
182 * @param {!Network.NetworkLogView.Filter} filter
183 * @param {!SDK.NetworkRequest} request
184 * @return {boolean}
185 */
186 static _negativeFilter(filter, request) {
187 return !filter(request);
188 }
189
190 /**
191 * @param {?RegExp} regex
192 * @param {!SDK.NetworkRequest} request
193 * @return {boolean}
194 */
195 static _requestPathFilter(regex, request) {
196 if (!regex)
197 return false;
198
199 return regex.test(request.path() + '/' + request.name());
200 }
201
202 /**
203 * @param {string} domain
204 * @return {!Array.<string>}
205 */
206 static _subdomains(domain) {
207 const result = [domain];
208 let indexOfPeriod = domain.indexOf('.');
209 while (indexOfPeriod !== -1) {
210 result.push('*' + domain.substring(indexOfPeriod));
211 indexOfPeriod = domain.indexOf('.', indexOfPeriod + 1);
212 }
213 return result;
214 }
215
216 /**
217 * @param {string} value
218 * @return {!Network.NetworkLogView.Filter}
219 */
220 static _createRequestDomainFilter(value) {
221 /**
222 * @param {string} string
223 * @return {string}
224 */
225 function escapeForRegExp(string) {
226 return string.escapeForRegExp();
227 }
228 const escapedPattern = value.split('*').map(escapeForRegExp).join('.*');
229 return Network.NetworkLogView._requestDomainFilter.bind(null, new RegExp('^' + escapedPattern + '$', 'i'));
230 }
231
232 /**
233 * @param {!RegExp} regex
234 * @param {!SDK.NetworkRequest} request
235 * @return {boolean}
236 */
237 static _requestDomainFilter(regex, request) {
238 return regex.test(request.domain);
239 }
240
241 /**
242 * @param {!SDK.NetworkRequest} request
243 * @return {boolean}
244 */
245 static _runningRequestFilter(request) {
246 return !request.finished;
247 }
248
249 /**
250 * @param {!SDK.NetworkRequest} request
251 * @return {boolean}
252 */
253 static _fromCacheRequestFilter(request) {
254 return request.cached();
255 }
256
257 /**
Joey Arhard183e7e2019-02-28 03:37:05258 * @param {!SDK.NetworkRequest} request
259 * @return {boolean}
260 */
261 static _interceptedByServiceWorkerFilter(request) {
262 return request.fetchedViaServiceWorker;
263 }
264
265 /**
266 * @param {!SDK.NetworkRequest} request
267 * @return {boolean}
268 */
269 static _initiatedByServiceWorkerFilter(request) {
270 return request.initiatedByServiceWorker();
271 }
272
273 /**
Blink Reformat4c46d092018-04-07 15:32:37274 * @param {string} value
275 * @param {!SDK.NetworkRequest} request
276 * @return {boolean}
277 */
278 static _requestResponseHeaderFilter(value, request) {
279 return request.responseHeaderValue(value) !== undefined;
280 }
281
282 /**
283 * @param {string} value
284 * @param {!SDK.NetworkRequest} request
285 * @return {boolean}
286 */
287 static _requestMethodFilter(value, request) {
288 return request.requestMethod === value;
289 }
290
291 /**
292 * @param {string} value
293 * @param {!SDK.NetworkRequest} request
294 * @return {boolean}
295 */
296 static _requestPriorityFilter(value, request) {
297 return request.priority() === value;
298 }
299
300 /**
301 * @param {string} value
302 * @param {!SDK.NetworkRequest} request
303 * @return {boolean}
304 */
305 static _requestMimeTypeFilter(value, request) {
306 return request.mimeType === value;
307 }
308
309 /**
310 * @param {!Network.NetworkLogView.MixedContentFilterValues} value
311 * @param {!SDK.NetworkRequest} request
312 * @return {boolean}
313 */
314 static _requestMixedContentFilter(value, request) {
315 if (value === Network.NetworkLogView.MixedContentFilterValues.Displayed)
316 return request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable;
317 else if (value === Network.NetworkLogView.MixedContentFilterValues.Blocked)
318 return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && request.wasBlocked();
319 else if (value === Network.NetworkLogView.MixedContentFilterValues.BlockOverridden)
320 return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && !request.wasBlocked();
321 else if (value === Network.NetworkLogView.MixedContentFilterValues.All)
322 return request.mixedContentType !== Protocol.Security.MixedContentType.None;
323
324 return false;
325 }
326
327 /**
328 * @param {string} value
329 * @param {!SDK.NetworkRequest} request
330 * @return {boolean}
331 */
332 static _requestSchemeFilter(value, request) {
333 return request.scheme === value;
334 }
335
336 /**
337 * @param {string} value
338 * @param {!SDK.NetworkRequest} request
339 * @return {boolean}
340 */
341 static _requestSetCookieDomainFilter(value, request) {
342 const cookies = request.responseCookies;
343 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
344 if (cookies[i].domain() === value)
345 return true;
346 }
347 return false;
348 }
349
350 /**
351 * @param {string} value
352 * @param {!SDK.NetworkRequest} request
353 * @return {boolean}
354 */
355 static _requestSetCookieNameFilter(value, request) {
356 const cookies = request.responseCookies;
357 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
358 if (cookies[i].name() === value)
359 return true;
360 }
361 return false;
362 }
363
364 /**
365 * @param {string} value
366 * @param {!SDK.NetworkRequest} request
367 * @return {boolean}
368 */
369 static _requestSetCookieValueFilter(value, request) {
370 const cookies = request.responseCookies;
371 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
372 if (cookies[i].value() === value)
373 return true;
374 }
375 return false;
376 }
377
378 /**
379 * @param {number} value
380 * @param {!SDK.NetworkRequest} request
381 * @return {boolean}
382 */
383 static _requestSizeLargerThanFilter(value, request) {
384 return request.transferSize >= value;
385 }
386
387 /**
388 * @param {string} value
389 * @param {!SDK.NetworkRequest} request
390 * @return {boolean}
391 */
392 static _statusCodeFilter(value, request) {
393 return ('' + request.statusCode) === value;
394 }
395
396 /**
397 * @param {!SDK.NetworkRequest} request
398 * @return {boolean}
399 */
400 static HTTPRequestsFilter(request) {
401 return request.parsedURL.isValid && (request.scheme in Network.NetworkLogView.HTTPSchemas);
402 }
403
404 /**
405 * @param {!SDK.NetworkRequest} request
406 * @return {boolean}
407 */
408 static FinishedRequestsFilter(request) {
409 return request.finished;
410 }
411
412 /**
413 * @param {number} windowStart
414 * @param {number} windowEnd
415 * @param {!SDK.NetworkRequest} request
416 * @return {boolean}
417 */
418 static _requestTimeFilter(windowStart, windowEnd, request) {
419 if (request.issueTime() > windowEnd)
420 return false;
421 if (request.endTime !== -1 && request.endTime < windowStart)
422 return false;
423 return true;
424 }
425
426 /**
427 * @param {!SDK.NetworkRequest} request
428 */
429 static _copyRequestHeaders(request) {
430 InspectorFrontendHost.copyText(request.requestHeadersText());
431 }
432
433 /**
434 * @param {!SDK.NetworkRequest} request
435 */
436 static _copyResponseHeaders(request) {
437 InspectorFrontendHost.copyText(request.responseHeadersText);
438 }
439
440 /**
441 * @param {!SDK.NetworkRequest} request
442 */
443 static async _copyResponse(request) {
444 const contentData = await request.contentData();
Ingvar Stepanyan1c771842018-10-10 14:35:08445 let content = contentData.content || '';
446 if (!request.contentType().isTextType())
447 content = Common.ContentProvider.contentAsDataURL(content, request.mimeType, contentData.encoded);
448 else if (contentData.encoded)
449 content = window.atob(content);
450 InspectorFrontendHost.copyText(content);
Blink Reformat4c46d092018-04-07 15:32:37451 }
452
453 /**
454 * @param {!DataTransfer} dataTransfer
455 */
456 _handleDrop(dataTransfer) {
457 const items = dataTransfer.items;
458 if (!items.length)
459 return;
460 const entry = items[0].webkitGetAsEntry();
461 if (entry.isDirectory)
462 return;
463
464 entry.file(this._onLoadFromFile.bind(this));
465 }
466
467 /**
468 * @param {!File} file
469 */
470 async _onLoadFromFile(file) {
471 const outputStream = new Common.StringOutputStream();
472 const reader = new Bindings.ChunkedFileReader(file, /* chunkSize */ 10000000);
473 const success = await reader.read(outputStream);
474 if (!success) {
475 this._harLoadFailed(reader.error().message);
476 return;
477 }
478 let harRoot;
479 try {
480 // HARRoot and JSON.parse might throw.
481 harRoot = new HARImporter.HARRoot(JSON.parse(outputStream.data()));
482 } catch (e) {
483 this._harLoadFailed(e);
484 return;
485 }
Pavel Feldman18d13562018-07-31 03:31:18486 SDK.networkLog.importRequests(HARImporter.Importer.requestsFromHARLog(harRoot.log));
Blink Reformat4c46d092018-04-07 15:32:37487 }
488
489 /**
490 * @param {string} message
491 */
492 _harLoadFailed(message) {
493 Common.console.error('Failed to load HAR file with following error: ' + message);
494 }
495
496 /**
497 * @param {?string} groupKey
498 */
499 _setGrouping(groupKey) {
500 if (this._activeGroupLookup)
501 this._activeGroupLookup.reset();
502 const groupLookup = groupKey ? this._groupLookups.get(groupKey) || null : null;
503 this._activeGroupLookup = groupLookup;
504 this._invalidateAllItems();
505 }
506
507 /**
508 * @return {number}
509 */
510 _computeRowHeight() {
511 return Math.round(this._rawRowHeight * window.devicePixelRatio) / window.devicePixelRatio;
512 }
513
514 /**
515 * @param {!SDK.NetworkRequest} request
516 * @return {?Network.NetworkRequestNode}
517 */
518 nodeForRequest(request) {
519 return request[Network.NetworkLogView._networkNodeSymbol] || null;
520 }
521
522 /**
523 * @return {number}
524 */
525 headerHeight() {
526 return this._headerHeight;
527 }
528
529 /**
530 * @param {boolean} recording
531 */
532 setRecording(recording) {
533 this._recording = recording;
534 this._updateSummaryBar();
535 }
536
537 /**
538 * @override
539 * @param {!SDK.NetworkManager} networkManager
540 */
541 modelAdded(networkManager) {
542 // TODO(allada) Remove dependency on networkManager and instead use NetworkLog and PageLoad for needed data.
543 if (networkManager.target().parentTarget())
544 return;
545 const resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
546 if (resourceTreeModel) {
547 resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
548 resourceTreeModel.addEventListener(
549 SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
550 }
551 }
552
553 /**
554 * @override
555 * @param {!SDK.NetworkManager} networkManager
556 */
557 modelRemoved(networkManager) {
558 if (!networkManager.target().parentTarget()) {
559 const resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
560 if (resourceTreeModel) {
561 resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
562 resourceTreeModel.removeEventListener(
563 SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
564 }
565 }
566 }
567
568 /**
569 * @param {number} start
570 * @param {number} end
571 */
572 setWindow(start, end) {
573 if (!start && !end) {
574 this._timeFilter = null;
575 this._timeCalculator.setWindow(null);
576 } else {
577 this._timeFilter = Network.NetworkLogView._requestTimeFilter.bind(null, start, end);
578 this._timeCalculator.setWindow(new Network.NetworkTimeBoundary(start, end));
579 }
580 this._filterRequests();
581 }
582
583 clearSelection() {
584 if (this._dataGrid.selectedNode)
585 this._dataGrid.selectedNode.deselect();
586 }
587
588 _resetSuggestionBuilder() {
589 this._suggestionBuilder.clear();
590 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.Running);
591 this._suggestionBuilder.addItem(
592 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.FromCache);
Joey Arhard183e7e2019-02-28 03:37:05593 this._suggestionBuilder.addItem(
594 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.ServiceWorkerIntercepted);
595 this._suggestionBuilder.addItem(
596 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.ServiceWorkerInitiated);
Blink Reformat4c46d092018-04-07 15:32:37597 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '100');
598 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '10k');
599 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '1M');
600 this._textFilterUI.setSuggestionProvider(this._suggestionBuilder.completions.bind(this._suggestionBuilder));
601 }
602
603 /**
604 * @param {!Common.Event} event
605 */
606 _filterChanged(event) {
607 this.removeAllNodeHighlights();
608 this._parseFilterQuery(this._textFilterUI.value());
609 this._filterRequests();
Blink Reformat4c46d092018-04-07 15:32:37610 }
611
612 _showRecordingHint() {
613 this._hideRecordingHint();
614 this._recordingHint = this.element.createChild('div', 'network-status-pane fill');
615 const hintText = this._recordingHint.createChild('div', 'recording-hint');
Joey Arhar0585e6f2018-10-30 23:11:18616
617 let reloadShortcutNode = null;
618 const reloadShortcutDescriptor = UI.shortcutRegistry.shortcutDescriptorsForAction('inspector_main.reload')[0];
619 if (reloadShortcutDescriptor) {
620 reloadShortcutNode = this._recordingHint.createChild('b');
621 reloadShortcutNode.textContent = reloadShortcutDescriptor.name;
622 }
Blink Reformat4c46d092018-04-07 15:32:37623
624 if (this._recording) {
625 const recordingText = hintText.createChild('span');
626 recordingText.textContent = Common.UIString('Recording network activity\u2026');
Joey Arhar0585e6f2018-10-30 23:11:18627 if (reloadShortcutNode) {
628 hintText.createChild('br');
629 hintText.appendChild(
630 UI.formatLocalized('Perform a request or hit %s to record the reload.', [reloadShortcutNode]));
631 }
Blink Reformat4c46d092018-04-07 15:32:37632 } else {
633 const recordNode = hintText.createChild('b');
634 recordNode.textContent = UI.shortcutRegistry.shortcutTitleForAction('network.toggle-recording');
Joey Arhar0585e6f2018-10-30 23:11:18635 if (reloadShortcutNode) {
636 hintText.appendChild(UI.formatLocalized(
637 'Record (%s) or reload (%s) to display network activity.', [recordNode, reloadShortcutNode]));
638 } else {
639 hintText.appendChild(UI.formatLocalized('Record (%s) to display network activity.', [recordNode]));
640 }
Blink Reformat4c46d092018-04-07 15:32:37641 }
Kayce Basques5444c1b2019-02-15 20:32:53642 hintText.createChild('br');
643 hintText.appendChild(UI.XLink.create(
644 'https://developers.google.com/web/tools/chrome-devtools/network/?utm_source=devtools&utm_campaign=2019Q1',
645 'Learn more'));
Blink Reformat4c46d092018-04-07 15:32:37646 }
647
648 _hideRecordingHint() {
649 if (this._recordingHint)
650 this._recordingHint.remove();
651 this._recordingHint = null;
652 }
653
654 /**
655 * @override
656 * @return {!Array.<!Element>}
657 */
658 elementsToRestoreScrollPositionsFor() {
659 if (!this._dataGrid) // Not initialized yet.
660 return [];
661 return [this._dataGrid.scrollContainer];
662 }
663
664 columnExtensionResolved() {
665 this._invalidateAllItems(true);
666 }
667
668 _setupDataGrid() {
669 this._dataGrid.setRowContextMenuCallback((contextMenu, node) => {
670 const request = node.request();
671 if (request)
672 this.handleContextMenuForRequest(contextMenu, request);
673 });
674 this._dataGrid.setStickToBottom(true);
675 this._dataGrid.setName('networkLog');
676 this._dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);
677 this._dataGrid.element.classList.add('network-log-grid');
678 this._dataGrid.element.addEventListener('mousedown', this._dataGridMouseDown.bind(this), true);
679 this._dataGrid.element.addEventListener('mousemove', this._dataGridMouseMove.bind(this), true);
680 this._dataGrid.element.addEventListener('mouseleave', () => this._setHoveredNode(null), true);
681 return this._dataGrid;
682 }
683
684 /**
685 * @param {!Event} event
686 */
687 _dataGridMouseMove(event) {
688 const node = (this._dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.target)));
689 const highlightInitiatorChain = event.shiftKey;
690 this._setHoveredNode(node, highlightInitiatorChain);
691 }
692
693 /**
694 * @return {?Network.NetworkNode}
695 */
696 hoveredNode() {
697 return this._hoveredNode;
698 }
699
700 /**
701 * @param {?Network.NetworkNode} node
702 * @param {boolean=} highlightInitiatorChain
703 */
704 _setHoveredNode(node, highlightInitiatorChain) {
705 if (this._hoveredNode)
706 this._hoveredNode.setHovered(false, false);
707 this._hoveredNode = node;
708 if (this._hoveredNode)
709 this._hoveredNode.setHovered(true, !!highlightInitiatorChain);
710 }
711
712 /**
713 * @param {!Event} event
714 */
715 _dataGridMouseDown(event) {
716 if (!this._dataGrid.selectedNode && event.button)
717 event.consume();
718 }
719
720 _updateSummaryBar() {
721 this._hideRecordingHint();
722
723 let transferSize = 0;
Dan Beam87466b52018-12-01 18:41:20724 let resourceSize = 0;
Blink Reformat4c46d092018-04-07 15:32:37725 let selectedNodeNumber = 0;
726 let selectedTransferSize = 0;
Dan Beam87466b52018-12-01 18:41:20727 let selectedResourceSize = 0;
Blink Reformat4c46d092018-04-07 15:32:37728 let baseTime = -1;
729 let maxTime = -1;
730
731 let nodeCount = 0;
Pavel Feldman18d13562018-07-31 03:31:18732 for (const request of SDK.networkLog.requests()) {
Blink Reformat4c46d092018-04-07 15:32:37733 const node = request[Network.NetworkLogView._networkNodeSymbol];
734 if (!node)
735 continue;
736 nodeCount++;
737 const requestTransferSize = request.transferSize;
738 transferSize += requestTransferSize;
Dan Beam87466b52018-12-01 18:41:20739 const requestResourceSize = request.resourceSize;
740 resourceSize += requestResourceSize;
Blink Reformat4c46d092018-04-07 15:32:37741 if (!node[Network.NetworkLogView._isFilteredOutSymbol]) {
742 selectedNodeNumber++;
743 selectedTransferSize += requestTransferSize;
Dan Beam87466b52018-12-01 18:41:20744 selectedResourceSize += requestResourceSize;
Blink Reformat4c46d092018-04-07 15:32:37745 }
746 const networkManager = SDK.NetworkManager.forRequest(request);
747 // TODO(allada) inspectedURL should be stored in PageLoad used instead of target so HAR requests can have an
748 // inspected url.
749 if (networkManager && request.url() === networkManager.target().inspectedURL() &&
750 request.resourceType() === Common.resourceTypes.Document && !networkManager.target().parentTarget())
751 baseTime = request.startTime;
752 if (request.endTime > maxTime)
753 maxTime = request.endTime;
754 }
755
756 if (!nodeCount) {
757 this._showRecordingHint();
758 return;
759 }
760
Joey Arhara86c14e2019-03-12 03:20:50761 this._summaryToolbar.removeToolbarItems();
Blink Reformat4c46d092018-04-07 15:32:37762 /**
763 * @param {string} chunk
Joey Arhara86c14e2019-03-12 03:20:50764 * @param {string=} title
Blink Reformat4c46d092018-04-07 15:32:37765 * @return {!Element}
766 */
Joey Arhara86c14e2019-03-12 03:20:50767 const appendChunk = (chunk, title) => {
768 const toolbarText = new UI.ToolbarText(chunk);
769 toolbarText.setTitle(title ? title : chunk);
770 this._summaryToolbar.appendToolbarItem(toolbarText);
771 return toolbarText.element;
772 };
Blink Reformat4c46d092018-04-07 15:32:37773
774 if (selectedNodeNumber !== nodeCount) {
Joey Arhara86c14e2019-03-12 03:20:50775 appendChunk(ls`${selectedNodeNumber} / ${nodeCount} requests`);
776 this._summaryToolbar.appendSeparator();
777 appendChunk(
778 ls`${Number.bytesToString(selectedTransferSize)} / ${Number.bytesToString(transferSize)} transferred`,
779 ls`${selectedTransferSize} B / ${transferSize} B transferred`);
780 this._summaryToolbar.appendSeparator();
781 appendChunk(
782 ls`${Number.bytesToString(selectedResourceSize)} / ${Number.bytesToString(resourceSize)} resources`,
783 ls`${selectedResourceSize} B / ${resourceSize} B resources`);
Blink Reformat4c46d092018-04-07 15:32:37784 } else {
Joey Arhara86c14e2019-03-12 03:20:50785 appendChunk(ls`${nodeCount} requests`);
786 this._summaryToolbar.appendSeparator();
787 appendChunk(ls`${Number.bytesToString(transferSize)} transferred`, ls`${transferSize} B transferred`);
788 this._summaryToolbar.appendSeparator();
789 appendChunk(ls`${Number.bytesToString(resourceSize)} resources`, ls`${resourceSize} B resources`);
Blink Reformat4c46d092018-04-07 15:32:37790 }
Dan Beam87466b52018-12-01 18:41:20791
Blink Reformat4c46d092018-04-07 15:32:37792 if (baseTime !== -1 && maxTime !== -1) {
Joey Arhara86c14e2019-03-12 03:20:50793 this._summaryToolbar.appendSeparator();
794 appendChunk(ls`Finish: ${Number.secondsToString(maxTime - baseTime)}`);
Blink Reformat4c46d092018-04-07 15:32:37795 if (this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
Joey Arhara86c14e2019-03-12 03:20:50796 this._summaryToolbar.appendSeparator();
Alexei Filippovfdcd8a62018-12-17 21:32:30797 const domContentLoadedText =
798 ls`DOMContentLoaded: ${Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime)}`;
Joey Arhara86c14e2019-03-12 03:20:50799 appendChunk(domContentLoadedText).style.color = Network.NetworkLogView.getDCLEventColor();
Blink Reformat4c46d092018-04-07 15:32:37800 }
801 if (this._mainRequestLoadTime !== -1) {
Joey Arhara86c14e2019-03-12 03:20:50802 this._summaryToolbar.appendSeparator();
Alexei Filippovfdcd8a62018-12-17 21:32:30803 const loadText = ls`Load: ${Number.secondsToString(this._mainRequestLoadTime - baseTime)}`;
Joey Arhara86c14e2019-03-12 03:20:50804 appendChunk(loadText).style.color = Network.NetworkLogView.getLoadEventColor();
Blink Reformat4c46d092018-04-07 15:32:37805 }
806 }
Blink Reformat4c46d092018-04-07 15:32:37807 }
808
809 scheduleRefresh() {
810 if (this._needsRefresh)
811 return;
812
813 this._needsRefresh = true;
814
815 if (this.isShowing() && !this._refreshRequestId)
816 this._refreshRequestId = this.element.window().requestAnimationFrame(this._refresh.bind(this));
817 }
818
819 /**
820 * @param {!Array<number>} times
821 */
822 addFilmStripFrames(times) {
823 this._columns.addEventDividers(times, 'network-frame-divider');
824 }
825
826 /**
827 * @param {number} time
828 */
829 selectFilmStripFrame(time) {
830 this._columns.selectFilmStripFrame(time);
831 }
832
833 clearFilmStripFrame() {
834 this._columns.clearFilmStripFrame();
835 }
836
837 _refreshIfNeeded() {
838 if (this._needsRefresh)
839 this._refresh();
840 }
841
842 /**
843 * @param {boolean=} deferUpdate
844 */
845 _invalidateAllItems(deferUpdate) {
Pavel Feldman18d13562018-07-31 03:31:18846 this._staleRequests = new Set(SDK.networkLog.requests());
Blink Reformat4c46d092018-04-07 15:32:37847 if (deferUpdate)
848 this.scheduleRefresh();
849 else
850 this._refresh();
851 }
852
853 /**
854 * @return {!Network.NetworkTimeCalculator}
855 */
856 timeCalculator() {
857 return this._timeCalculator;
858 }
859
860 /**
861 * @return {!Network.NetworkTimeCalculator}
862 */
863 calculator() {
864 return this._calculator;
865 }
866
867 /**
868 * @param {!Network.NetworkTimeCalculator} x
869 */
870 setCalculator(x) {
871 if (!x || this._calculator === x)
872 return;
873
874 if (this._calculator !== x) {
875 this._calculator = x;
876 this._columns.setCalculator(this._calculator);
877 }
878 this._calculator.reset();
879
880 if (this._calculator.startAtZero)
881 this._columns.hideEventDividers();
882 else
883 this._columns.showEventDividers();
884
885 this._invalidateAllItems();
886 }
887
888 /**
889 * @param {!Common.Event} event
890 */
891 _loadEventFired(event) {
892 if (!this._recording)
893 return;
894
895 const time = /** @type {number} */ (event.data.loadTime);
896 if (time) {
897 this._mainRequestLoadTime = time;
Alexei Filippovfdcd8a62018-12-17 21:32:30898 this._columns.addEventDividers([time], 'network-load-divider');
Blink Reformat4c46d092018-04-07 15:32:37899 }
900 }
901
902 /**
903 * @param {!Common.Event} event
904 */
905 _domContentLoadedEventFired(event) {
906 if (!this._recording)
907 return;
908 const data = /** @type {number} */ (event.data);
909 if (data) {
910 this._mainRequestDOMContentLoadedTime = data;
Alexei Filippovfdcd8a62018-12-17 21:32:30911 this._columns.addEventDividers([data], 'network-dcl-divider');
Blink Reformat4c46d092018-04-07 15:32:37912 }
913 }
914
915 /**
916 * @override
917 */
918 wasShown() {
919 this._refreshIfNeeded();
920 this._columns.wasShown();
921 }
922
923 /**
924 * @override
925 */
926 willHide() {
927 this._columns.willHide();
928 }
929
930 /**
931 * @override
932 */
933 onResize() {
934 this._rowHeight = this._computeRowHeight();
935 }
936
937 /**
938 * @return {!Array<!Network.NetworkNode>}
939 */
940 flatNodesList() {
941 return this._dataGrid.rootNode().flatChildren();
942 }
943
944 stylesChanged() {
945 this._columns.scheduleRefresh();
946 }
947
948 _refresh() {
949 this._needsRefresh = false;
950
951 if (this._refreshRequestId) {
952 this.element.window().cancelAnimationFrame(this._refreshRequestId);
953 this._refreshRequestId = null;
954 }
955
956 this.removeAllNodeHighlights();
957
958 this._timeCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
959 this._durationCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
960 this._timeCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
961 this._durationCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
962
963 /** @type {!Map<!Network.NetworkNode, !Network.NetworkNode>} */
964 const nodesToInsert = new Map();
965 /** @type {!Array<!Network.NetworkNode>} */
966 const nodesToRefresh = [];
967
968 /** @type {!Set<!Network.NetworkRequestNode>} */
969 const staleNodes = new Set();
970
971 // While creating nodes it may add more entries into _staleRequests because redirect request nodes update the parent
972 // node so we loop until we have no more stale requests.
973 while (this._staleRequests.size) {
974 const request = this._staleRequests.firstValue();
975 this._staleRequests.delete(request);
976 let node = request[Network.NetworkLogView._networkNodeSymbol];
977 if (!node)
978 node = this._createNodeForRequest(request);
979 staleNodes.add(node);
980 }
981
982 for (const node of staleNodes) {
983 const isFilteredOut = !this._applyFilter(node);
984 if (isFilteredOut && node === this._hoveredNode)
985 this._setHoveredNode(null);
986
987 if (!isFilteredOut)
988 nodesToRefresh.push(node);
989 const request = node.request();
990 this._timeCalculator.updateBoundaries(request);
991 this._durationCalculator.updateBoundaries(request);
992 const newParent = this._parentNodeForInsert(node);
993 if (node[Network.NetworkLogView._isFilteredOutSymbol] === isFilteredOut && node.parent === newParent)
994 continue;
995 node[Network.NetworkLogView._isFilteredOutSymbol] = isFilteredOut;
996 const removeFromParent = node.parent && (isFilteredOut || node.parent !== newParent);
997 if (removeFromParent) {
998 let parent = node.parent;
999 parent.removeChild(node);
1000 while (parent && !parent.hasChildren() && parent.dataGrid && parent.dataGrid.rootNode() !== parent) {
1001 const grandparent = parent.parent;
1002 grandparent.removeChild(parent);
1003 parent = grandparent;
1004 }
1005 }
1006
1007 if (!newParent || isFilteredOut)
1008 continue;
1009
1010 if (!newParent.dataGrid && !nodesToInsert.has(newParent)) {
1011 nodesToInsert.set(newParent, this._dataGrid.rootNode());
1012 nodesToRefresh.push(newParent);
1013 }
1014 nodesToInsert.set(node, newParent);
1015 }
1016
1017 for (const node of nodesToInsert.keys())
1018 nodesToInsert.get(node).appendChild(node);
1019
1020 for (const node of nodesToRefresh)
1021 node.refresh();
1022
1023 this._updateSummaryBar();
1024
1025 if (nodesToInsert.size)
1026 this._columns.sortByCurrentColumn();
1027
1028 this._dataGrid.updateInstantly();
1029 this._didRefreshForTest();
1030 }
1031
1032 _didRefreshForTest() {
1033 }
1034
1035 /**
1036 * @param {!Network.NetworkRequestNode} node
1037 * @return {?Network.NetworkNode}
1038 */
1039 _parentNodeForInsert(node) {
1040 if (!this._activeGroupLookup)
1041 return this._dataGrid.rootNode();
1042
1043 const groupNode = this._activeGroupLookup.groupNodeForRequest(node.request());
1044 if (!groupNode)
1045 return this._dataGrid.rootNode();
1046 return groupNode;
1047 }
1048
1049 _reset() {
1050 this.dispatchEventToListeners(Network.NetworkLogView.Events.RequestSelected, null);
1051
1052 this._setHoveredNode(null);
1053 this._columns.reset();
1054
1055 this._timeFilter = null;
1056 this._calculator.reset();
1057
1058 this._timeCalculator.setWindow(null);
1059 this.linkifier.reset();
1060 this.badgePool.reset();
1061
1062 if (this._activeGroupLookup)
1063 this._activeGroupLookup.reset();
1064 this._staleRequests.clear();
1065 this._resetSuggestionBuilder();
1066
1067 this._mainRequestLoadTime = -1;
1068 this._mainRequestDOMContentLoadedTime = -1;
1069
1070 this._dataGrid.rootNode().removeChildren();
1071 this._updateSummaryBar();
1072 this._dataGrid.setStickToBottom(true);
1073 this.scheduleRefresh();
1074 }
1075
1076 /**
1077 * @param {string} filterString
1078 */
1079 setTextFilterValue(filterString) {
1080 this._textFilterUI.setValue(filterString);
1081 this._dataURLFilterUI.setChecked(false);
1082 this._resourceCategoryFilterUI.reset();
1083 }
1084
1085 /**
1086 * @param {!SDK.NetworkRequest} request
1087 */
1088 _createNodeForRequest(request) {
1089 const node = new Network.NetworkRequestNode(this, request);
1090 request[Network.NetworkLogView._networkNodeSymbol] = node;
1091 node[Network.NetworkLogView._isFilteredOutSymbol] = true;
1092
1093 for (let redirect = request.redirectSource(); redirect; redirect = redirect.redirectSource())
1094 this._refreshRequest(redirect);
1095 return node;
1096 }
1097
1098 /**
1099 * @param {!Common.Event} event
1100 */
1101 _onRequestUpdated(event) {
1102 const request = /** @type {!SDK.NetworkRequest} */ (event.data);
1103 this._refreshRequest(request);
1104 }
1105
1106 /**
1107 * @param {!SDK.NetworkRequest} request
1108 */
1109 _refreshRequest(request) {
1110 Network.NetworkLogView._subdomains(request.domain)
1111 .forEach(
1112 this._suggestionBuilder.addItem.bind(this._suggestionBuilder, Network.NetworkLogView.FilterType.Domain));
1113 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Method, request.requestMethod);
1114 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MimeType, request.mimeType);
1115 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Scheme, '' + request.scheme);
1116 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.StatusCode, '' + request.statusCode);
1117
1118 const priority = request.priority();
1119 if (priority) {
1120 this._suggestionBuilder.addItem(
1121 Network.NetworkLogView.FilterType.Priority, PerfUI.uiLabelForNetworkPriority(priority));
1122 }
1123
1124 if (request.mixedContentType !== Protocol.Security.MixedContentType.None) {
1125 this._suggestionBuilder.addItem(
1126 Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.All);
1127 }
1128
1129 if (request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable) {
1130 this._suggestionBuilder.addItem(
1131 Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.Displayed);
1132 }
1133
1134 if (request.mixedContentType === Protocol.Security.MixedContentType.Blockable) {
1135 const suggestion = request.wasBlocked() ? Network.NetworkLogView.MixedContentFilterValues.Blocked :
1136 Network.NetworkLogView.MixedContentFilterValues.BlockOverridden;
1137 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MixedContent, suggestion);
1138 }
1139
1140 const responseHeaders = request.responseHeaders;
1141 for (let i = 0, l = responseHeaders.length; i < l; ++i)
1142 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
1143 const cookies = request.responseCookies;
1144 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1145 const cookie = cookies[i];
1146 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
1147 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieName, cookie.name());
1148 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieValue, cookie.value());
1149 }
1150
1151 this._staleRequests.add(request);
1152 this.scheduleRefresh();
1153 }
1154
1155 /**
1156 * @return {number}
1157 */
1158 rowHeight() {
1159 return this._rowHeight;
1160 }
1161
1162 /**
1163 * @param {boolean} gridMode
1164 */
1165 switchViewMode(gridMode) {
1166 this._columns.switchViewMode(gridMode);
1167 }
1168
1169 /**
1170 * @param {!UI.ContextMenu} contextMenu
1171 * @param {!SDK.NetworkRequest} request
1172 */
1173 handleContextMenuForRequest(contextMenu, request) {
1174 contextMenu.appendApplicableItems(request);
1175 let copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
1176 const footerSection = copyMenu.footerSection();
1177 if (request) {
1178 copyMenu.defaultSection().appendItem(
1179 UI.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, request.contentURL()));
1180 if (request.requestHeadersText()) {
1181 copyMenu.defaultSection().appendItem(
1182 Common.UIString('Copy request headers'), Network.NetworkLogView._copyRequestHeaders.bind(null, request));
1183 }
1184
1185 if (request.responseHeadersText) {
1186 copyMenu.defaultSection().appendItem(
1187 Common.UIString('Copy response headers'), Network.NetworkLogView._copyResponseHeaders.bind(null, request));
1188 }
1189
1190 if (request.finished) {
1191 copyMenu.defaultSection().appendItem(
1192 Common.UIString('Copy response'), Network.NetworkLogView._copyResponse.bind(null, request));
1193 }
1194
Harley Libcf41f92018-09-10 18:01:131195 const disableIfBlob = request.isBlobRequest();
Blink Reformat4c46d092018-04-07 15:32:371196 if (Host.isWin()) {
1197 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131198 Common.UIString('Copy as PowerShell'), this._copyPowerShellCommand.bind(this, request), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371199 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131200 Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371201 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131202 Common.UIString('Copy as cURL (cmd)'), this._copyCurlCommand.bind(this, request, 'win'), disableIfBlob);
1203 footerSection.appendItem(
1204 Common.UIString('Copy as cURL (bash)'), this._copyCurlCommand.bind(this, request, 'unix'), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371205 footerSection.appendItem(Common.UIString('Copy all as PowerShell'), this._copyAllPowerShellCommand.bind(this));
1206 footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
1207 footerSection.appendItem(Common.UIString('Copy all as cURL (cmd)'), this._copyAllCurlCommand.bind(this, 'win'));
1208 footerSection.appendItem(
1209 Common.UIString('Copy all as cURL (bash)'), this._copyAllCurlCommand.bind(this, 'unix'));
1210 } else {
Harley Libcf41f92018-09-10 18:01:131211 footerSection.appendItem(
1212 Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request), disableIfBlob);
1213 footerSection.appendItem(
1214 Common.UIString('Copy as cURL'), this._copyCurlCommand.bind(this, request, 'unix'), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371215 footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
1216 footerSection.appendItem(Common.UIString('Copy all as cURL'), this._copyAllCurlCommand.bind(this, 'unix'));
1217 }
1218 } else {
1219 copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
1220 }
1221 footerSection.appendItem(Common.UIString('Copy all as HAR'), this._copyAll.bind(this));
1222
Joey Arhar076efbc2018-11-06 00:24:201223 contextMenu.saveSection().appendItem(Common.UIString('Save all as HAR with content'), this._exportAll.bind(this));
Blink Reformat4c46d092018-04-07 15:32:371224
1225 contextMenu.editSection().appendItem(Common.UIString('Clear browser cache'), this._clearBrowserCache.bind(this));
1226 contextMenu.editSection().appendItem(
1227 Common.UIString('Clear browser cookies'), this._clearBrowserCookies.bind(this));
1228
1229 if (request) {
1230 const maxBlockedURLLength = 20;
1231 const manager = SDK.multitargetNetworkManager;
1232 let patterns = manager.blockedPatterns();
1233
1234 const urlWithoutScheme = request.parsedURL.urlWithoutScheme();
1235 if (urlWithoutScheme && !patterns.find(pattern => pattern.url === urlWithoutScheme)) {
1236 contextMenu.debugSection().appendItem(
1237 Common.UIString('Block request URL'), addBlockedURL.bind(null, urlWithoutScheme));
1238 } else if (urlWithoutScheme) {
1239 const croppedURL = urlWithoutScheme.trimMiddle(maxBlockedURLLength);
1240 contextMenu.debugSection().appendItem(
1241 Common.UIString('Unblock %s', croppedURL), removeBlockedURL.bind(null, urlWithoutScheme));
1242 }
1243
1244 const domain = request.parsedURL.domain();
1245 if (domain && !patterns.find(pattern => pattern.url === domain)) {
1246 contextMenu.debugSection().appendItem(
1247 Common.UIString('Block request domain'), addBlockedURL.bind(null, domain));
1248 } else if (domain) {
1249 const croppedDomain = domain.trimMiddle(maxBlockedURLLength);
1250 contextMenu.debugSection().appendItem(
1251 Common.UIString('Unblock %s', croppedDomain), removeBlockedURL.bind(null, domain));
1252 }
1253
1254 if (SDK.NetworkManager.canReplayRequest(request)) {
1255 contextMenu.debugSection().appendItem(
1256 Common.UIString('Replay XHR'), SDK.NetworkManager.replayRequest.bind(null, request));
1257 }
1258
1259 /**
1260 * @param {string} url
1261 */
1262 function addBlockedURL(url) {
1263 patterns.push({enabled: true, url: url});
1264 manager.setBlockedPatterns(patterns);
1265 manager.setBlockingEnabled(true);
1266 UI.viewManager.showView('network.blocked-urls');
1267 }
1268
1269 /**
1270 * @param {string} url
1271 */
1272 function removeBlockedURL(url) {
1273 patterns = patterns.filter(pattern => pattern.url !== url);
1274 manager.setBlockedPatterns(patterns);
1275 UI.viewManager.showView('network.blocked-urls');
1276 }
1277 }
1278 }
1279
1280 _harRequests() {
Pavel Feldman18d13562018-07-31 03:31:181281 const httpRequests = SDK.networkLog.requests().filter(Network.NetworkLogView.HTTPRequestsFilter);
Blink Reformat4c46d092018-04-07 15:32:371282 return httpRequests.filter(Network.NetworkLogView.FinishedRequestsFilter);
1283 }
1284
1285 async _copyAll() {
Pavel Feldman18d13562018-07-31 03:31:181286 const harArchive = {log: await SDK.HARLog.build(this._harRequests())};
Blink Reformat4c46d092018-04-07 15:32:371287 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1288 }
1289
1290 /**
1291 * @param {!SDK.NetworkRequest} request
1292 * @param {string} platform
1293 */
1294 async _copyCurlCommand(request, platform) {
1295 const command = await this._generateCurlCommand(request, platform);
1296 InspectorFrontendHost.copyText(command);
1297 }
1298
1299 /**
1300 * @param {string} platform
1301 */
1302 async _copyAllCurlCommand(platform) {
Harley Libcf41f92018-09-10 18:01:131303 const commands = await this._generateAllCurlCommand(SDK.networkLog.requests(), platform);
1304 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371305 }
1306
1307 /**
1308 * @param {!SDK.NetworkRequest} request
1309 * @param {string} platform
1310 */
1311 async _copyFetchCall(request, platform) {
1312 const command = await this._generateFetchCall(request);
1313 InspectorFrontendHost.copyText(command);
1314 }
1315
1316 async _copyAllFetchCall() {
Harley Libcf41f92018-09-10 18:01:131317 const commands = await this._generateAllFetchCall(SDK.networkLog.requests());
1318 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371319 }
1320
1321 /**
1322 * @param {!SDK.NetworkRequest} request
1323 */
1324 async _copyPowerShellCommand(request) {
1325 const command = await this._generatePowerShellCommand(request);
1326 InspectorFrontendHost.copyText(command);
1327 }
1328
1329 async _copyAllPowerShellCommand() {
Harley Libcf41f92018-09-10 18:01:131330 const commands = this._generateAllPowerShellCommand(SDK.networkLog.requests());
1331 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371332 }
1333
1334 async _exportAll() {
1335 const url = SDK.targetManager.mainTarget().inspectedURL();
1336 const parsedURL = url.asParsedURL();
1337 const filename = parsedURL ? parsedURL.host : 'network-log';
1338 const stream = new Bindings.FileOutputStream();
1339
1340 if (!await stream.open(filename + '.har'))
1341 return;
1342
1343 const progressIndicator = new UI.ProgressIndicator();
1344 this._progressBarContainer.appendChild(progressIndicator.element);
1345 await Network.HARWriter.write(stream, this._harRequests(), progressIndicator);
1346 progressIndicator.done();
1347 stream.close();
1348 }
1349
1350 _clearBrowserCache() {
1351 if (confirm(Common.UIString('Are you sure you want to clear browser cache?')))
1352 SDK.multitargetNetworkManager.clearBrowserCache();
1353 }
1354
1355 _clearBrowserCookies() {
1356 if (confirm(Common.UIString('Are you sure you want to clear browser cookies?')))
1357 SDK.multitargetNetworkManager.clearBrowserCookies();
1358 }
1359
1360 _removeAllHighlights() {
1361 this.removeAllNodeHighlights();
1362 for (let i = 0; i < this._highlightedSubstringChanges.length; ++i)
1363 UI.revertDomChanges(this._highlightedSubstringChanges[i]);
1364 this._highlightedSubstringChanges = [];
1365 }
1366
1367 /**
1368 * @param {!Network.NetworkRequestNode} node
1369 * @return {boolean}
1370 */
1371 _applyFilter(node) {
1372 const request = node.request();
1373 if (this._timeFilter && !this._timeFilter(request))
1374 return false;
1375 const categoryName = request.resourceType().category().title;
1376 if (!this._resourceCategoryFilterUI.accept(categoryName))
1377 return false;
Joey Arharba99d622019-02-01 19:10:481378 if (this._dataURLFilterUI.checked() && (request.parsedURL.isDataURL() || request.parsedURL.isBlobURL()))
Blink Reformat4c46d092018-04-07 15:32:371379 return false;
1380 if (request.statusText === 'Service Worker Fallback Required')
1381 return false;
1382 for (let i = 0; i < this._filters.length; ++i) {
1383 if (!this._filters[i](request))
1384 return false;
1385 }
1386 return true;
1387 }
1388
1389 /**
1390 * @param {string} query
1391 */
1392 _parseFilterQuery(query) {
1393 const descriptors = this._filterParser.parse(query);
1394 this._filters = descriptors.map(descriptor => {
1395 const key = descriptor.key;
1396 const text = descriptor.text || '';
1397 const regex = descriptor.regex;
1398 let filter;
1399 if (key) {
1400 const defaultText = (key + ':' + text).escapeForRegExp();
1401 filter = this._createSpecialFilter(/** @type {!Network.NetworkLogView.FilterType} */ (key), text) ||
1402 Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(defaultText, 'i'));
1403 } else if (descriptor.regex) {
1404 filter = Network.NetworkLogView._requestPathFilter.bind(null, /** @type {!RegExp} */ (regex));
1405 } else {
1406 filter = Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(text.escapeForRegExp(), 'i'));
1407 }
1408 return descriptor.negative ? Network.NetworkLogView._negativeFilter.bind(null, filter) : filter;
1409 });
1410 }
1411
1412 /**
1413 * @param {!Network.NetworkLogView.FilterType} type
1414 * @param {string} value
1415 * @return {?Network.NetworkLogView.Filter}
1416 */
1417 _createSpecialFilter(type, value) {
1418 switch (type) {
1419 case Network.NetworkLogView.FilterType.Domain:
1420 return Network.NetworkLogView._createRequestDomainFilter(value);
1421
1422 case Network.NetworkLogView.FilterType.HasResponseHeader:
1423 return Network.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1424
1425 case Network.NetworkLogView.FilterType.Is:
1426 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.Running)
1427 return Network.NetworkLogView._runningRequestFilter;
1428 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.FromCache)
1429 return Network.NetworkLogView._fromCacheRequestFilter;
Joey Arhard183e7e2019-02-28 03:37:051430 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.ServiceWorkerIntercepted)
1431 return Network.NetworkLogView._interceptedByServiceWorkerFilter;
1432 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.ServiceWorkerInitiated)
1433 return Network.NetworkLogView._initiatedByServiceWorkerFilter;
Blink Reformat4c46d092018-04-07 15:32:371434 break;
1435
1436 case Network.NetworkLogView.FilterType.LargerThan:
1437 return this._createSizeFilter(value.toLowerCase());
1438
1439 case Network.NetworkLogView.FilterType.Method:
1440 return Network.NetworkLogView._requestMethodFilter.bind(null, value);
1441
1442 case Network.NetworkLogView.FilterType.MimeType:
1443 return Network.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1444
1445 case Network.NetworkLogView.FilterType.MixedContent:
1446 return Network.NetworkLogView._requestMixedContentFilter.bind(
1447 null, /** @type {!Network.NetworkLogView.MixedContentFilterValues} */ (value));
1448
1449 case Network.NetworkLogView.FilterType.Scheme:
1450 return Network.NetworkLogView._requestSchemeFilter.bind(null, value);
1451
1452 case Network.NetworkLogView.FilterType.SetCookieDomain:
1453 return Network.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1454
1455 case Network.NetworkLogView.FilterType.SetCookieName:
1456 return Network.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1457
1458 case Network.NetworkLogView.FilterType.SetCookieValue:
1459 return Network.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1460
1461 case Network.NetworkLogView.FilterType.Priority:
1462 return Network.NetworkLogView._requestPriorityFilter.bind(null, PerfUI.uiLabelToNetworkPriority(value));
1463
1464 case Network.NetworkLogView.FilterType.StatusCode:
1465 return Network.NetworkLogView._statusCodeFilter.bind(null, value);
1466 }
1467 return null;
1468 }
1469
1470 /**
1471 * @param {string} value
1472 * @return {?Network.NetworkLogView.Filter}
1473 */
1474 _createSizeFilter(value) {
1475 let multiplier = 1;
1476 if (value.endsWith('k')) {
1477 multiplier = 1024;
1478 value = value.substring(0, value.length - 1);
1479 } else if (value.endsWith('m')) {
1480 multiplier = 1024 * 1024;
1481 value = value.substring(0, value.length - 1);
1482 }
1483 const quantity = Number(value);
1484 if (isNaN(quantity))
1485 return null;
1486 return Network.NetworkLogView._requestSizeLargerThanFilter.bind(null, quantity * multiplier);
1487 }
1488
1489 _filterRequests() {
1490 this._removeAllHighlights();
1491 this._invalidateAllItems();
1492 }
1493
1494 /**
1495 * @param {!SDK.NetworkRequest} request
1496 * @return {?Network.NetworkRequestNode}
1497 */
1498 _reveal(request) {
1499 this.removeAllNodeHighlights();
1500 const node = request[Network.NetworkLogView._networkNodeSymbol];
1501 if (!node || !node.dataGrid)
1502 return null;
1503 node.reveal();
1504 return node;
1505 }
1506
1507 /**
1508 * @param {!SDK.NetworkRequest} request
1509 */
1510 revealAndHighlightRequest(request) {
1511 const node = this._reveal(request);
1512 if (node)
1513 this._highlightNode(node);
1514 }
1515
1516 /**
1517 * @param {!SDK.NetworkRequest} request
1518 */
1519 selectRequest(request) {
Eugene Ostroukhovb600f662018-05-09 00:18:141520 this.setTextFilterValue('');
Blink Reformat4c46d092018-04-07 15:32:371521 const node = this._reveal(request);
1522 if (node)
1523 node.select();
1524 }
1525
1526 removeAllNodeHighlights() {
1527 if (this._highlightedNode) {
1528 this._highlightedNode.element().classList.remove('highlighted-row');
1529 this._highlightedNode = null;
1530 }
1531 }
1532
1533 /**
1534 * @param {!Network.NetworkRequestNode} node
1535 */
1536 _highlightNode(node) {
1537 UI.runCSSAnimationOnce(node.element(), 'highlighted-row');
1538 this._highlightedNode = node;
1539 }
1540
1541 /**
Harley Libcf41f92018-09-10 18:01:131542 * @param {!Array<!SDK.NetworkRequest>} requests
1543 * @return {!Array<!SDK.NetworkRequest>}
1544 */
1545 _filterOutBlobRequests(requests) {
1546 return requests.filter(request => !request.isBlobRequest());
1547 }
1548
1549 /**
Blink Reformat4c46d092018-04-07 15:32:371550 * @param {!SDK.NetworkRequest} request
1551 * @return {!Promise<string>}
1552 */
1553 async _generateFetchCall(request) {
1554 const ignoredHeaders = {
1555 // Internal headers
1556 'method': 1,
1557 'path': 1,
1558 'scheme': 1,
1559 'version': 1,
1560
1561 // Unsafe headers
1562 // Keep this list synchronized with src/net/http/http_util.cc
1563 'accept-charset': 1,
1564 'accept-encoding': 1,
1565 'access-control-request-headers': 1,
1566 'access-control-request-method': 1,
1567 'connection': 1,
1568 'content-length': 1,
1569 'cookie': 1,
1570 'cookie2': 1,
1571 'date': 1,
1572 'dnt': 1,
1573 'expect': 1,
1574 'host': 1,
1575 'keep-alive': 1,
1576 'origin': 1,
1577 'referer': 1,
1578 'te': 1,
1579 'trailer': 1,
1580 'transfer-encoding': 1,
1581 'upgrade': 1,
1582 'via': 1,
1583 // TODO(phistuck) - remove this once crbug.com/571722 is fixed.
1584 'user-agent': 1
1585 };
1586
1587 const credentialHeaders = {'cookie': 1, 'authorization': 1};
1588
1589 const url = JSON.stringify(request.url());
1590
1591 const requestHeaders = request.requestHeaders();
1592 const headerData = requestHeaders.reduce((result, header) => {
1593 const name = header.name;
1594
1595 if (!ignoredHeaders[name.toLowerCase()] && !name.includes(':'))
1596 result.append(name, header.value);
1597
1598 return result;
1599 }, new Headers());
1600
1601 const headers = {};
1602 for (const headerArray of headerData)
PhistucK6ed0a3e2018-08-04 06:28:411603 headers[headerArray[0]] = headerArray[1];
Blink Reformat4c46d092018-04-07 15:32:371604
1605 const credentials =
1606 request.requestCookies || requestHeaders.some(({name}) => credentialHeaders[name.toLowerCase()]) ? 'include' :
1607 'omit';
1608
1609 const referrerHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'referer');
1610
1611 const referrer = referrerHeader ? referrerHeader.value : void 0;
1612
1613 const referrerPolicy = request.referrerPolicy() || void 0;
1614
1615 const requestBody = await request.requestFormData();
1616
1617 const fetchOptions = {
1618 credentials,
PhistucK6ed0a3e2018-08-04 06:28:411619 headers: Object.keys(headers).length ? headers : void 0,
Blink Reformat4c46d092018-04-07 15:32:371620 referrer,
1621 referrerPolicy,
1622 body: requestBody,
1623 method: request.requestMethod,
1624 mode: 'cors'
1625 };
1626
1627 const options = JSON.stringify(fetchOptions);
1628 return `fetch(${url}, ${options});`;
1629 }
1630
1631 /**
Harley Libcf41f92018-09-10 18:01:131632 * @param {!Array<!SDK.NetworkRequest>} requests
1633 * @return {!Promise<string>}
1634 */
1635 async _generateAllFetchCall(requests) {
1636 const nonBlobRequests = this._filterOutBlobRequests(requests);
1637 const commands = await Promise.all(nonBlobRequests.map(request => this._generateFetchCall(request)));
1638 return commands.join(' ;\n');
1639 }
1640
1641 /**
Blink Reformat4c46d092018-04-07 15:32:371642 * @param {!SDK.NetworkRequest} request
1643 * @param {string} platform
1644 * @return {!Promise<string>}
1645 */
1646 async _generateCurlCommand(request, platform) {
1647 let command = ['curl'];
1648 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1649 const ignoredHeaders = {'host': 1, 'method': 1, 'path': 1, 'scheme': 1, 'version': 1};
1650
1651 function escapeStringWin(str) {
1652 /* If there are no new line characters do not escape the " characters
1653 since it only uglifies the command.
1654
1655 Because cmd.exe parser and MS Crt arguments parsers use some of the
1656 same escape characters, they can interact with each other in
1657 horrible ways, the order of operations is critical.
1658
1659 Replace \ with \\ first because it is an escape character for certain
1660 conditions in both parsers.
1661
1662 Replace all " with \" to ensure the first parser does not remove it.
1663
1664 Then escape all characters we are not sure about with ^ to ensure it
1665 gets to MS Crt parser safely.
1666
1667 The % character is special because MS Crt parser will try and look for
1668 ENV variables and fill them in it's place. We cannot escape them with %
1669 and cannot escape them with ^ (because it's cmd.exe's escape not MS Crt
1670 parser); So we can get cmd.exe parser to escape the character after it,
1671 if it is followed by a valid beginning character of an ENV variable.
1672 This ensures we do not try and double escape another ^ if it was placed
1673 by the previous replace.
1674
1675 Lastly we replace new lines with ^ and TWO new lines because the first
1676 new line is there to enact the escape command the second is the character
1677 to escape (in this case new line).
1678 */
1679 const encapsChars = /[\r\n]/.test(str) ? '^"' : '"';
1680 return encapsChars +
1681 str.replace(/\\/g, '\\\\')
1682 .replace(/"/g, '\\"')
1683 .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
1684 .replace(/%(?=[a-zA-Z0-9_])/g, '%^')
1685 .replace(/\r\n|[\n\r]/g, '^\n\n') +
1686 encapsChars;
1687 }
1688
1689 /**
1690 * @param {string} str
1691 * @return {string}
1692 */
1693 function escapeStringPosix(str) {
1694 /**
1695 * @param {string} x
1696 * @return {string}
1697 */
1698 function escapeCharacter(x) {
Erik Luoaa676752018-08-21 05:52:221699 const code = x.charCodeAt(0);
1700 // Add leading zero when needed to not care about the next character.
1701 return code < 16 ? '\\u0' + code.toString(16) : '\\u' + code.toString(16);
Blink Reformat4c46d092018-04-07 15:32:371702 }
1703
Joey Arhar512e3742019-01-25 21:33:541704 if (/[\u0000-\u001f\u007f-\u009f!]|\'/.test(str)) {
Blink Reformat4c46d092018-04-07 15:32:371705 // Use ANSI-C quoting syntax.
1706 return '$\'' +
1707 str.replace(/\\/g, '\\\\')
1708 .replace(/\'/g, '\\\'')
1709 .replace(/\n/g, '\\n')
1710 .replace(/\r/g, '\\r')
Joey Arhar512e3742019-01-25 21:33:541711 .replace(/[\u0000-\u001f\u007f-\u009f!]/g, escapeCharacter) +
Blink Reformat4c46d092018-04-07 15:32:371712 '\'';
1713 } else {
1714 // Use single quote syntax.
1715 return '\'' + str + '\'';
1716 }
1717 }
1718
1719 // cURL command expected to run on the same platform that DevTools run
1720 // (it may be different from the inspected page platform).
1721 const escapeString = platform === 'win' ? escapeStringWin : escapeStringPosix;
1722
1723 command.push(escapeString(request.url()).replace(/[[{}\]]/g, '\\$&'));
1724
1725 let inferredMethod = 'GET';
1726 const data = [];
1727 const requestContentType = request.requestContentType();
1728 const formData = await request.requestFormData();
1729 if (requestContentType && requestContentType.startsWith('application/x-www-form-urlencoded') && formData) {
1730 data.push('--data');
1731 data.push(escapeString(formData));
1732 ignoredHeaders['content-length'] = true;
1733 inferredMethod = 'POST';
1734 } else if (formData) {
1735 data.push('--data-binary');
1736 data.push(escapeString(formData));
1737 ignoredHeaders['content-length'] = true;
1738 inferredMethod = 'POST';
1739 }
1740
1741 if (request.requestMethod !== inferredMethod) {
1742 command.push('-X');
1743 command.push(request.requestMethod);
1744 }
1745
1746 const requestHeaders = request.requestHeaders();
1747 for (let i = 0; i < requestHeaders.length; i++) {
1748 const header = requestHeaders[i];
1749 const name = header.name.replace(/^:/, ''); // Translate SPDY v3 headers to HTTP headers.
1750 if (name.toLowerCase() in ignoredHeaders)
1751 continue;
1752 command.push('-H');
1753 command.push(escapeString(name + ': ' + header.value));
1754 }
1755 command = command.concat(data);
1756 command.push('--compressed');
1757
1758 if (request.securityState() === Protocol.Security.SecurityState.Insecure)
1759 command.push('--insecure');
1760 return command.join(' ');
1761 }
1762
1763 /**
Harley Libcf41f92018-09-10 18:01:131764 * @param {!Array<!SDK.NetworkRequest>} requests
1765 * @param {string} platform
1766 * @return {!Promise<string>}
1767 */
1768 async _generateAllCurlCommand(requests, platform) {
1769 const nonBlobRequests = this._filterOutBlobRequests(requests);
1770 const commands = await Promise.all(nonBlobRequests.map(request => this._generateCurlCommand(request, platform)));
1771 if (platform === 'win')
1772 return commands.join(' &\r\n');
1773 else
1774 return commands.join(' ;\n');
1775 }
1776
1777 /**
Blink Reformat4c46d092018-04-07 15:32:371778 * @param {!SDK.NetworkRequest} request
1779 * @return {!Promise<string>}
1780 */
1781 async _generatePowerShellCommand(request) {
1782 const command = ['Invoke-WebRequest'];
1783 const ignoredHeaders =
1784 new Set(['host', 'connection', 'proxy-connection', 'content-length', 'expect', 'range', 'content-type']);
1785
1786 /**
1787 * @param {string} str
1788 * @return {string}
1789 */
1790 function escapeString(str) {
1791 return '"' +
1792 str.replace(/[`\$"]/g, '`$&').replace(/[^\x20-\x7E]/g, char => '$([char]' + char.charCodeAt(0) + ')') + '"';
1793 }
1794
1795 command.push('-Uri');
1796 command.push(escapeString(request.url()));
1797
1798 if (request.requestMethod !== 'GET') {
1799 command.push('-Method');
1800 command.push(escapeString(request.requestMethod));
1801 }
1802
1803 const requestHeaders = request.requestHeaders();
1804 const headerNameValuePairs = [];
1805 for (const header of requestHeaders) {
1806 const name = header.name.replace(/^:/, ''); // Translate h2 headers to HTTP headers.
1807 if (ignoredHeaders.has(name.toLowerCase()))
1808 continue;
1809 headerNameValuePairs.push(escapeString(name) + '=' + escapeString(header.value));
1810 }
1811 if (headerNameValuePairs.length) {
1812 command.push('-Headers');
1813 command.push('@{' + headerNameValuePairs.join('; ') + '}');
1814 }
1815
1816 const contentTypeHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'content-type');
1817 if (contentTypeHeader) {
1818 command.push('-ContentType');
1819 command.push(escapeString(contentTypeHeader.value));
1820 }
1821
1822 const formData = await request.requestFormData();
1823 if (formData) {
1824 command.push('-Body');
1825 const body = escapeString(formData);
1826 if (/[^\x20-\x7E]/.test(formData))
1827 command.push('([System.Text.Encoding]::UTF8.GetBytes(' + body + '))');
1828 else
1829 command.push(body);
1830 }
1831
1832 return command.join(' ');
1833 }
Harley Libcf41f92018-09-10 18:01:131834
1835 /**
1836 * @param {!Array<!SDK.NetworkRequest>} requests
1837 * @return {!Promise<string>}
1838 */
1839 async _generateAllPowerShellCommand(requests) {
1840 const nonBlobRequests = this._filterOutBlobRequests(requests);
1841 const commands = await Promise.all(nonBlobRequests.map(request => this._generatePowerShellCommand(request)));
1842 return commands.join(';\r\n');
1843 }
Joey Arhara86c14e2019-03-12 03:20:501844
1845 /**
1846 * @return {string}
1847 */
1848 static getDCLEventColor() {
1849 if (UI.themeSupport.themeName() === 'dark')
1850 return '#03A9F4';
1851 return '#0867CB';
1852 }
1853
1854 /**
1855 * @return {string}
1856 */
1857 static getLoadEventColor() {
1858 return UI.themeSupport.patchColorText('#B31412', UI.ThemeSupport.ColorUsage.Foreground);
1859 }
Blink Reformat4c46d092018-04-07 15:32:371860};
1861
1862Network.NetworkLogView._isFilteredOutSymbol = Symbol('isFilteredOut');
1863Network.NetworkLogView._networkNodeSymbol = Symbol('NetworkNode');
1864
1865Network.NetworkLogView.HTTPSchemas = {
1866 'http': true,
1867 'https': true,
1868 'ws': true,
1869 'wss': true
1870};
1871
1872/** @enum {symbol} */
1873Network.NetworkLogView.Events = {
1874 RequestSelected: Symbol('RequestSelected')
1875};
1876
1877/** @enum {string} */
1878Network.NetworkLogView.FilterType = {
1879 Domain: 'domain',
1880 HasResponseHeader: 'has-response-header',
1881 Is: 'is',
1882 LargerThan: 'larger-than',
1883 Method: 'method',
1884 MimeType: 'mime-type',
1885 MixedContent: 'mixed-content',
1886 Priority: 'priority',
1887 Scheme: 'scheme',
1888 SetCookieDomain: 'set-cookie-domain',
1889 SetCookieName: 'set-cookie-name',
1890 SetCookieValue: 'set-cookie-value',
1891 StatusCode: 'status-code'
1892};
1893
1894/** @enum {string} */
1895Network.NetworkLogView.MixedContentFilterValues = {
1896 All: 'all',
1897 Displayed: 'displayed',
1898 Blocked: 'blocked',
1899 BlockOverridden: 'block-overridden'
1900};
1901
1902/** @enum {string} */
1903Network.NetworkLogView.IsFilterType = {
1904 Running: 'running',
Joey Arhard183e7e2019-02-28 03:37:051905 FromCache: 'from-cache',
1906 ServiceWorkerIntercepted: 'service-worker-intercepted',
1907 ServiceWorkerInitiated: 'service-worker-initiated'
Blink Reformat4c46d092018-04-07 15:32:371908};
1909
1910/** @type {!Array<string>} */
1911Network.NetworkLogView._searchKeys =
1912 Object.keys(Network.NetworkLogView.FilterType).map(key => Network.NetworkLogView.FilterType[key]);
1913
1914/** @typedef {function(!SDK.NetworkRequest): boolean} */
1915Network.NetworkLogView.Filter;
1916
1917/**
1918 * @interface
1919 */
1920Network.GroupLookupInterface = function() {};
1921
1922Network.GroupLookupInterface.prototype = {
1923 /**
1924 * @param {!SDK.NetworkRequest} request
1925 * @return {?Network.NetworkGroupNode}
1926 */
1927 groupNodeForRequest: function(request) {},
1928
1929 reset: function() {}
1930};