blob: 287f3912bc3a86b0f64522bd2ad21fb27c30ecda [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);
Brandon Goddard568cef12019-06-27 17:18:20126 UI.ARIAUtils.setAccessibleName(this._resourceCategoryFilterUI.element(), ls`Resource types to include`);
Blink Reformat4c46d092018-04-07 15:32:37127 this._resourceCategoryFilterUI.addEventListener(
128 UI.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
129 filterBar.addFilter(this._resourceCategoryFilterUI);
130
131 this._filterParser = new TextUtils.FilterParser(Network.NetworkLogView._searchKeys);
132 this._suggestionBuilder =
133 new UI.FilterSuggestionBuilder(Network.NetworkLogView._searchKeys, Network.NetworkLogView._sortSearchValues);
134 this._resetSuggestionBuilder();
135
136 this._dataGrid = this._columns.dataGrid();
137 this._setupDataGrid();
138 this._columns.sortByCurrentColumn();
Erik Luo0187a022018-05-31 18:35:49139 filterBar.filterButton().addEventListener(
140 UI.ToolbarButton.Events.Click, this._dataGrid.scheduleUpdate.bind(this._dataGrid, true /* isFromUser */));
Blink Reformat4c46d092018-04-07 15:32:37141
Joey Arhara86c14e2019-03-12 03:20:50142 this._summaryToolbar = new UI.Toolbar('network-summary-bar', this.element);
Blink Reformat4c46d092018-04-07 15:32:37143
144 new UI.DropTarget(
145 this.element, [UI.DropTarget.Type.File], Common.UIString('Drop HAR files here'), this._handleDrop.bind(this));
146
147 Common.moduleSetting('networkColorCodeResourceTypes')
148 .addChangeListener(this._invalidateAllItems.bind(this, false), this);
149
150 SDK.targetManager.observeModels(SDK.NetworkManager, this);
Pavel Feldman18d13562018-07-31 03:31:18151 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestAdded, this._onRequestUpdated, this);
152 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.RequestUpdated, this._onRequestUpdated, this);
153 SDK.networkLog.addEventListener(SDK.NetworkLog.Events.Reset, this._reset, this);
Blink Reformat4c46d092018-04-07 15:32:37154
155 this._updateGroupByFrame();
156 Common.moduleSetting('network.group-by-frame').addChangeListener(() => this._updateGroupByFrame());
157
158 this._filterBar = filterBar;
Blink Reformat4c46d092018-04-07 15:32:37159 }
160
Blink Reformat4c46d092018-04-07 15:32:37161 _updateGroupByFrame() {
162 const value = Common.moduleSetting('network.group-by-frame').get();
163 this._setGrouping(value ? 'Frame' : null);
164 }
165
166 /**
167 * @param {string} key
168 * @param {!Array<string>} values
169 */
170 static _sortSearchValues(key, values) {
171 if (key === Network.NetworkLogView.FilterType.Priority) {
172 values.sort((a, b) => {
173 const aPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(a));
174 const bPriority = /** @type {!Protocol.Network.ResourcePriority} */ (PerfUI.uiLabelToNetworkPriority(b));
175 return PerfUI.networkPriorityWeight(aPriority) - PerfUI.networkPriorityWeight(bPriority);
176 });
177 } else {
178 values.sort();
179 }
180 }
181
182 /**
183 * @param {!Network.NetworkLogView.Filter} filter
184 * @param {!SDK.NetworkRequest} request
185 * @return {boolean}
186 */
187 static _negativeFilter(filter, request) {
188 return !filter(request);
189 }
190
191 /**
192 * @param {?RegExp} regex
193 * @param {!SDK.NetworkRequest} request
194 * @return {boolean}
195 */
196 static _requestPathFilter(regex, request) {
197 if (!regex)
198 return false;
199
200 return regex.test(request.path() + '/' + request.name());
201 }
202
203 /**
204 * @param {string} domain
205 * @return {!Array.<string>}
206 */
207 static _subdomains(domain) {
208 const result = [domain];
209 let indexOfPeriod = domain.indexOf('.');
210 while (indexOfPeriod !== -1) {
211 result.push('*' + domain.substring(indexOfPeriod));
212 indexOfPeriod = domain.indexOf('.', indexOfPeriod + 1);
213 }
214 return result;
215 }
216
217 /**
218 * @param {string} value
219 * @return {!Network.NetworkLogView.Filter}
220 */
221 static _createRequestDomainFilter(value) {
222 /**
223 * @param {string} string
224 * @return {string}
225 */
226 function escapeForRegExp(string) {
227 return string.escapeForRegExp();
228 }
229 const escapedPattern = value.split('*').map(escapeForRegExp).join('.*');
230 return Network.NetworkLogView._requestDomainFilter.bind(null, new RegExp('^' + escapedPattern + '$', 'i'));
231 }
232
233 /**
234 * @param {!RegExp} regex
235 * @param {!SDK.NetworkRequest} request
236 * @return {boolean}
237 */
238 static _requestDomainFilter(regex, request) {
239 return regex.test(request.domain);
240 }
241
242 /**
243 * @param {!SDK.NetworkRequest} request
244 * @return {boolean}
245 */
246 static _runningRequestFilter(request) {
247 return !request.finished;
248 }
249
250 /**
251 * @param {!SDK.NetworkRequest} request
252 * @return {boolean}
253 */
254 static _fromCacheRequestFilter(request) {
255 return request.cached();
256 }
257
258 /**
Joey Arhard183e7e2019-02-28 03:37:05259 * @param {!SDK.NetworkRequest} request
260 * @return {boolean}
261 */
262 static _interceptedByServiceWorkerFilter(request) {
263 return request.fetchedViaServiceWorker;
264 }
265
266 /**
267 * @param {!SDK.NetworkRequest} request
268 * @return {boolean}
269 */
270 static _initiatedByServiceWorkerFilter(request) {
271 return request.initiatedByServiceWorker();
272 }
273
274 /**
Blink Reformat4c46d092018-04-07 15:32:37275 * @param {string} value
276 * @param {!SDK.NetworkRequest} request
277 * @return {boolean}
278 */
279 static _requestResponseHeaderFilter(value, request) {
280 return request.responseHeaderValue(value) !== undefined;
281 }
282
283 /**
284 * @param {string} value
285 * @param {!SDK.NetworkRequest} request
286 * @return {boolean}
287 */
288 static _requestMethodFilter(value, request) {
289 return request.requestMethod === value;
290 }
291
292 /**
293 * @param {string} value
294 * @param {!SDK.NetworkRequest} request
295 * @return {boolean}
296 */
297 static _requestPriorityFilter(value, request) {
298 return request.priority() === value;
299 }
300
301 /**
302 * @param {string} value
303 * @param {!SDK.NetworkRequest} request
304 * @return {boolean}
305 */
306 static _requestMimeTypeFilter(value, request) {
307 return request.mimeType === value;
308 }
309
310 /**
311 * @param {!Network.NetworkLogView.MixedContentFilterValues} value
312 * @param {!SDK.NetworkRequest} request
313 * @return {boolean}
314 */
315 static _requestMixedContentFilter(value, request) {
316 if (value === Network.NetworkLogView.MixedContentFilterValues.Displayed)
317 return request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable;
318 else if (value === Network.NetworkLogView.MixedContentFilterValues.Blocked)
319 return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && request.wasBlocked();
320 else if (value === Network.NetworkLogView.MixedContentFilterValues.BlockOverridden)
321 return request.mixedContentType === Protocol.Security.MixedContentType.Blockable && !request.wasBlocked();
322 else if (value === Network.NetworkLogView.MixedContentFilterValues.All)
323 return request.mixedContentType !== Protocol.Security.MixedContentType.None;
324
325 return false;
326 }
327
328 /**
329 * @param {string} value
330 * @param {!SDK.NetworkRequest} request
331 * @return {boolean}
332 */
333 static _requestSchemeFilter(value, request) {
334 return request.scheme === value;
335 }
336
337 /**
338 * @param {string} value
339 * @param {!SDK.NetworkRequest} request
340 * @return {boolean}
341 */
342 static _requestSetCookieDomainFilter(value, request) {
343 const cookies = request.responseCookies;
344 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
345 if (cookies[i].domain() === value)
346 return true;
347 }
348 return false;
349 }
350
351 /**
352 * @param {string} value
353 * @param {!SDK.NetworkRequest} request
354 * @return {boolean}
355 */
356 static _requestSetCookieNameFilter(value, request) {
357 const cookies = request.responseCookies;
358 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
359 if (cookies[i].name() === value)
360 return true;
361 }
362 return false;
363 }
364
365 /**
366 * @param {string} value
367 * @param {!SDK.NetworkRequest} request
368 * @return {boolean}
369 */
370 static _requestSetCookieValueFilter(value, request) {
371 const cookies = request.responseCookies;
372 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
373 if (cookies[i].value() === value)
374 return true;
375 }
376 return false;
377 }
378
379 /**
380 * @param {number} value
381 * @param {!SDK.NetworkRequest} request
382 * @return {boolean}
383 */
384 static _requestSizeLargerThanFilter(value, request) {
385 return request.transferSize >= value;
386 }
387
388 /**
389 * @param {string} value
390 * @param {!SDK.NetworkRequest} request
391 * @return {boolean}
392 */
393 static _statusCodeFilter(value, request) {
394 return ('' + request.statusCode) === value;
395 }
396
397 /**
398 * @param {!SDK.NetworkRequest} request
399 * @return {boolean}
400 */
401 static HTTPRequestsFilter(request) {
402 return request.parsedURL.isValid && (request.scheme in Network.NetworkLogView.HTTPSchemas);
403 }
404
405 /**
Blink Reformat4c46d092018-04-07 15:32:37406 * @param {number} windowStart
407 * @param {number} windowEnd
408 * @param {!SDK.NetworkRequest} request
409 * @return {boolean}
410 */
411 static _requestTimeFilter(windowStart, windowEnd, request) {
412 if (request.issueTime() > windowEnd)
413 return false;
414 if (request.endTime !== -1 && request.endTime < windowStart)
415 return false;
416 return true;
417 }
418
419 /**
420 * @param {!SDK.NetworkRequest} request
421 */
422 static _copyRequestHeaders(request) {
423 InspectorFrontendHost.copyText(request.requestHeadersText());
424 }
425
426 /**
427 * @param {!SDK.NetworkRequest} request
428 */
429 static _copyResponseHeaders(request) {
430 InspectorFrontendHost.copyText(request.responseHeadersText);
431 }
432
433 /**
434 * @param {!SDK.NetworkRequest} request
435 */
436 static async _copyResponse(request) {
437 const contentData = await request.contentData();
Ingvar Stepanyan1c771842018-10-10 14:35:08438 let content = contentData.content || '';
439 if (!request.contentType().isTextType())
440 content = Common.ContentProvider.contentAsDataURL(content, request.mimeType, contentData.encoded);
441 else if (contentData.encoded)
442 content = window.atob(content);
443 InspectorFrontendHost.copyText(content);
Blink Reformat4c46d092018-04-07 15:32:37444 }
445
446 /**
447 * @param {!DataTransfer} dataTransfer
448 */
449 _handleDrop(dataTransfer) {
450 const items = dataTransfer.items;
451 if (!items.length)
452 return;
453 const entry = items[0].webkitGetAsEntry();
454 if (entry.isDirectory)
455 return;
456
Joey Arhar0e1093c2019-05-21 00:34:22457 entry.file(this.onLoadFromFile.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37458 }
459
460 /**
461 * @param {!File} file
462 */
Joey Arhar0e1093c2019-05-21 00:34:22463 async onLoadFromFile(file) {
Blink Reformat4c46d092018-04-07 15:32:37464 const outputStream = new Common.StringOutputStream();
465 const reader = new Bindings.ChunkedFileReader(file, /* chunkSize */ 10000000);
466 const success = await reader.read(outputStream);
467 if (!success) {
468 this._harLoadFailed(reader.error().message);
469 return;
470 }
471 let harRoot;
472 try {
473 // HARRoot and JSON.parse might throw.
474 harRoot = new HARImporter.HARRoot(JSON.parse(outputStream.data()));
475 } catch (e) {
476 this._harLoadFailed(e);
477 return;
478 }
Pavel Feldman18d13562018-07-31 03:31:18479 SDK.networkLog.importRequests(HARImporter.Importer.requestsFromHARLog(harRoot.log));
Blink Reformat4c46d092018-04-07 15:32:37480 }
481
482 /**
483 * @param {string} message
484 */
485 _harLoadFailed(message) {
486 Common.console.error('Failed to load HAR file with following error: ' + message);
487 }
488
489 /**
490 * @param {?string} groupKey
491 */
492 _setGrouping(groupKey) {
493 if (this._activeGroupLookup)
494 this._activeGroupLookup.reset();
495 const groupLookup = groupKey ? this._groupLookups.get(groupKey) || null : null;
496 this._activeGroupLookup = groupLookup;
497 this._invalidateAllItems();
498 }
499
500 /**
501 * @return {number}
502 */
503 _computeRowHeight() {
504 return Math.round(this._rawRowHeight * window.devicePixelRatio) / window.devicePixelRatio;
505 }
506
507 /**
508 * @param {!SDK.NetworkRequest} request
509 * @return {?Network.NetworkRequestNode}
510 */
511 nodeForRequest(request) {
512 return request[Network.NetworkLogView._networkNodeSymbol] || null;
513 }
514
515 /**
516 * @return {number}
517 */
518 headerHeight() {
519 return this._headerHeight;
520 }
521
522 /**
523 * @param {boolean} recording
524 */
525 setRecording(recording) {
526 this._recording = recording;
527 this._updateSummaryBar();
528 }
529
530 /**
531 * @override
532 * @param {!SDK.NetworkManager} networkManager
533 */
534 modelAdded(networkManager) {
535 // TODO(allada) Remove dependency on networkManager and instead use NetworkLog and PageLoad for needed data.
536 if (networkManager.target().parentTarget())
537 return;
538 const resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
539 if (resourceTreeModel) {
540 resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
541 resourceTreeModel.addEventListener(
542 SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
543 }
544 }
545
546 /**
547 * @override
548 * @param {!SDK.NetworkManager} networkManager
549 */
550 modelRemoved(networkManager) {
551 if (!networkManager.target().parentTarget()) {
552 const resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
553 if (resourceTreeModel) {
554 resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this);
555 resourceTreeModel.removeEventListener(
556 SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
557 }
558 }
559 }
560
561 /**
562 * @param {number} start
563 * @param {number} end
564 */
565 setWindow(start, end) {
566 if (!start && !end) {
567 this._timeFilter = null;
568 this._timeCalculator.setWindow(null);
569 } else {
570 this._timeFilter = Network.NetworkLogView._requestTimeFilter.bind(null, start, end);
571 this._timeCalculator.setWindow(new Network.NetworkTimeBoundary(start, end));
572 }
573 this._filterRequests();
574 }
575
576 clearSelection() {
577 if (this._dataGrid.selectedNode)
578 this._dataGrid.selectedNode.deselect();
579 }
580
581 _resetSuggestionBuilder() {
582 this._suggestionBuilder.clear();
583 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.Running);
584 this._suggestionBuilder.addItem(
585 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.FromCache);
Joey Arhard183e7e2019-02-28 03:37:05586 this._suggestionBuilder.addItem(
587 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.ServiceWorkerIntercepted);
588 this._suggestionBuilder.addItem(
589 Network.NetworkLogView.FilterType.Is, Network.NetworkLogView.IsFilterType.ServiceWorkerInitiated);
Blink Reformat4c46d092018-04-07 15:32:37590 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '100');
591 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '10k');
592 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '1M');
593 this._textFilterUI.setSuggestionProvider(this._suggestionBuilder.completions.bind(this._suggestionBuilder));
594 }
595
596 /**
597 * @param {!Common.Event} event
598 */
599 _filterChanged(event) {
600 this.removeAllNodeHighlights();
601 this._parseFilterQuery(this._textFilterUI.value());
602 this._filterRequests();
Blink Reformat4c46d092018-04-07 15:32:37603 }
604
605 _showRecordingHint() {
606 this._hideRecordingHint();
607 this._recordingHint = this.element.createChild('div', 'network-status-pane fill');
608 const hintText = this._recordingHint.createChild('div', 'recording-hint');
Joey Arhar0585e6f2018-10-30 23:11:18609
610 let reloadShortcutNode = null;
611 const reloadShortcutDescriptor = UI.shortcutRegistry.shortcutDescriptorsForAction('inspector_main.reload')[0];
612 if (reloadShortcutDescriptor) {
613 reloadShortcutNode = this._recordingHint.createChild('b');
614 reloadShortcutNode.textContent = reloadShortcutDescriptor.name;
615 }
Blink Reformat4c46d092018-04-07 15:32:37616
617 if (this._recording) {
618 const recordingText = hintText.createChild('span');
619 recordingText.textContent = Common.UIString('Recording network activity\u2026');
Joey Arhar0585e6f2018-10-30 23:11:18620 if (reloadShortcutNode) {
621 hintText.createChild('br');
622 hintText.appendChild(
623 UI.formatLocalized('Perform a request or hit %s to record the reload.', [reloadShortcutNode]));
624 }
Blink Reformat4c46d092018-04-07 15:32:37625 } else {
626 const recordNode = hintText.createChild('b');
627 recordNode.textContent = UI.shortcutRegistry.shortcutTitleForAction('network.toggle-recording');
Joey Arhar0585e6f2018-10-30 23:11:18628 if (reloadShortcutNode) {
629 hintText.appendChild(UI.formatLocalized(
630 'Record (%s) or reload (%s) to display network activity.', [recordNode, reloadShortcutNode]));
631 } else {
632 hintText.appendChild(UI.formatLocalized('Record (%s) to display network activity.', [recordNode]));
633 }
Blink Reformat4c46d092018-04-07 15:32:37634 }
Kayce Basques5444c1b2019-02-15 20:32:53635 hintText.createChild('br');
636 hintText.appendChild(UI.XLink.create(
637 'https://developers.google.com/web/tools/chrome-devtools/network/?utm_source=devtools&utm_campaign=2019Q1',
638 'Learn more'));
Blink Reformat4c46d092018-04-07 15:32:37639 }
640
641 _hideRecordingHint() {
642 if (this._recordingHint)
643 this._recordingHint.remove();
644 this._recordingHint = null;
645 }
646
647 /**
648 * @override
649 * @return {!Array.<!Element>}
650 */
651 elementsToRestoreScrollPositionsFor() {
652 if (!this._dataGrid) // Not initialized yet.
653 return [];
654 return [this._dataGrid.scrollContainer];
655 }
656
657 columnExtensionResolved() {
658 this._invalidateAllItems(true);
659 }
660
661 _setupDataGrid() {
662 this._dataGrid.setRowContextMenuCallback((contextMenu, node) => {
663 const request = node.request();
664 if (request)
665 this.handleContextMenuForRequest(contextMenu, request);
666 });
667 this._dataGrid.setStickToBottom(true);
668 this._dataGrid.setName('networkLog');
669 this._dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);
670 this._dataGrid.element.classList.add('network-log-grid');
671 this._dataGrid.element.addEventListener('mousedown', this._dataGridMouseDown.bind(this), true);
672 this._dataGrid.element.addEventListener('mousemove', this._dataGridMouseMove.bind(this), true);
673 this._dataGrid.element.addEventListener('mouseleave', () => this._setHoveredNode(null), true);
674 return this._dataGrid;
675 }
676
677 /**
678 * @param {!Event} event
679 */
680 _dataGridMouseMove(event) {
681 const node = (this._dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.target)));
682 const highlightInitiatorChain = event.shiftKey;
683 this._setHoveredNode(node, highlightInitiatorChain);
684 }
685
686 /**
687 * @return {?Network.NetworkNode}
688 */
689 hoveredNode() {
690 return this._hoveredNode;
691 }
692
693 /**
694 * @param {?Network.NetworkNode} node
695 * @param {boolean=} highlightInitiatorChain
696 */
697 _setHoveredNode(node, highlightInitiatorChain) {
698 if (this._hoveredNode)
699 this._hoveredNode.setHovered(false, false);
700 this._hoveredNode = node;
701 if (this._hoveredNode)
702 this._hoveredNode.setHovered(true, !!highlightInitiatorChain);
703 }
704
705 /**
706 * @param {!Event} event
707 */
708 _dataGridMouseDown(event) {
709 if (!this._dataGrid.selectedNode && event.button)
710 event.consume();
711 }
712
713 _updateSummaryBar() {
714 this._hideRecordingHint();
715
716 let transferSize = 0;
Dan Beam87466b52018-12-01 18:41:20717 let resourceSize = 0;
Blink Reformat4c46d092018-04-07 15:32:37718 let selectedNodeNumber = 0;
719 let selectedTransferSize = 0;
Dan Beam87466b52018-12-01 18:41:20720 let selectedResourceSize = 0;
Blink Reformat4c46d092018-04-07 15:32:37721 let baseTime = -1;
722 let maxTime = -1;
723
724 let nodeCount = 0;
Pavel Feldman18d13562018-07-31 03:31:18725 for (const request of SDK.networkLog.requests()) {
Blink Reformat4c46d092018-04-07 15:32:37726 const node = request[Network.NetworkLogView._networkNodeSymbol];
727 if (!node)
728 continue;
729 nodeCount++;
730 const requestTransferSize = request.transferSize;
731 transferSize += requestTransferSize;
Dan Beam87466b52018-12-01 18:41:20732 const requestResourceSize = request.resourceSize;
733 resourceSize += requestResourceSize;
Blink Reformat4c46d092018-04-07 15:32:37734 if (!node[Network.NetworkLogView._isFilteredOutSymbol]) {
735 selectedNodeNumber++;
736 selectedTransferSize += requestTransferSize;
Dan Beam87466b52018-12-01 18:41:20737 selectedResourceSize += requestResourceSize;
Blink Reformat4c46d092018-04-07 15:32:37738 }
739 const networkManager = SDK.NetworkManager.forRequest(request);
740 // TODO(allada) inspectedURL should be stored in PageLoad used instead of target so HAR requests can have an
741 // inspected url.
742 if (networkManager && request.url() === networkManager.target().inspectedURL() &&
743 request.resourceType() === Common.resourceTypes.Document && !networkManager.target().parentTarget())
744 baseTime = request.startTime;
745 if (request.endTime > maxTime)
746 maxTime = request.endTime;
747 }
748
749 if (!nodeCount) {
750 this._showRecordingHint();
751 return;
752 }
753
Joey Arhara86c14e2019-03-12 03:20:50754 this._summaryToolbar.removeToolbarItems();
Blink Reformat4c46d092018-04-07 15:32:37755 /**
756 * @param {string} chunk
Joey Arhara86c14e2019-03-12 03:20:50757 * @param {string=} title
Blink Reformat4c46d092018-04-07 15:32:37758 * @return {!Element}
759 */
Joey Arhara86c14e2019-03-12 03:20:50760 const appendChunk = (chunk, title) => {
761 const toolbarText = new UI.ToolbarText(chunk);
762 toolbarText.setTitle(title ? title : chunk);
763 this._summaryToolbar.appendToolbarItem(toolbarText);
764 return toolbarText.element;
765 };
Blink Reformat4c46d092018-04-07 15:32:37766
767 if (selectedNodeNumber !== nodeCount) {
Joey Arhara86c14e2019-03-12 03:20:50768 appendChunk(ls`${selectedNodeNumber} / ${nodeCount} requests`);
769 this._summaryToolbar.appendSeparator();
770 appendChunk(
771 ls`${Number.bytesToString(selectedTransferSize)} / ${Number.bytesToString(transferSize)} transferred`,
772 ls`${selectedTransferSize} B / ${transferSize} B transferred`);
773 this._summaryToolbar.appendSeparator();
774 appendChunk(
775 ls`${Number.bytesToString(selectedResourceSize)} / ${Number.bytesToString(resourceSize)} resources`,
776 ls`${selectedResourceSize} B / ${resourceSize} B resources`);
Blink Reformat4c46d092018-04-07 15:32:37777 } else {
Joey Arhara86c14e2019-03-12 03:20:50778 appendChunk(ls`${nodeCount} requests`);
779 this._summaryToolbar.appendSeparator();
780 appendChunk(ls`${Number.bytesToString(transferSize)} transferred`, ls`${transferSize} B transferred`);
781 this._summaryToolbar.appendSeparator();
782 appendChunk(ls`${Number.bytesToString(resourceSize)} resources`, ls`${resourceSize} B resources`);
Blink Reformat4c46d092018-04-07 15:32:37783 }
Dan Beam87466b52018-12-01 18:41:20784
Blink Reformat4c46d092018-04-07 15:32:37785 if (baseTime !== -1 && maxTime !== -1) {
Joey Arhara86c14e2019-03-12 03:20:50786 this._summaryToolbar.appendSeparator();
787 appendChunk(ls`Finish: ${Number.secondsToString(maxTime - baseTime)}`);
Blink Reformat4c46d092018-04-07 15:32:37788 if (this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
Joey Arhara86c14e2019-03-12 03:20:50789 this._summaryToolbar.appendSeparator();
Alexei Filippovfdcd8a62018-12-17 21:32:30790 const domContentLoadedText =
791 ls`DOMContentLoaded: ${Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime)}`;
Joey Arhara86c14e2019-03-12 03:20:50792 appendChunk(domContentLoadedText).style.color = Network.NetworkLogView.getDCLEventColor();
Blink Reformat4c46d092018-04-07 15:32:37793 }
794 if (this._mainRequestLoadTime !== -1) {
Joey Arhara86c14e2019-03-12 03:20:50795 this._summaryToolbar.appendSeparator();
Alexei Filippovfdcd8a62018-12-17 21:32:30796 const loadText = ls`Load: ${Number.secondsToString(this._mainRequestLoadTime - baseTime)}`;
Joey Arhara86c14e2019-03-12 03:20:50797 appendChunk(loadText).style.color = Network.NetworkLogView.getLoadEventColor();
Blink Reformat4c46d092018-04-07 15:32:37798 }
799 }
Blink Reformat4c46d092018-04-07 15:32:37800 }
801
802 scheduleRefresh() {
803 if (this._needsRefresh)
804 return;
805
806 this._needsRefresh = true;
807
808 if (this.isShowing() && !this._refreshRequestId)
809 this._refreshRequestId = this.element.window().requestAnimationFrame(this._refresh.bind(this));
810 }
811
812 /**
813 * @param {!Array<number>} times
814 */
815 addFilmStripFrames(times) {
816 this._columns.addEventDividers(times, 'network-frame-divider');
817 }
818
819 /**
820 * @param {number} time
821 */
822 selectFilmStripFrame(time) {
823 this._columns.selectFilmStripFrame(time);
824 }
825
826 clearFilmStripFrame() {
827 this._columns.clearFilmStripFrame();
828 }
829
830 _refreshIfNeeded() {
831 if (this._needsRefresh)
832 this._refresh();
833 }
834
835 /**
836 * @param {boolean=} deferUpdate
837 */
838 _invalidateAllItems(deferUpdate) {
Pavel Feldman18d13562018-07-31 03:31:18839 this._staleRequests = new Set(SDK.networkLog.requests());
Blink Reformat4c46d092018-04-07 15:32:37840 if (deferUpdate)
841 this.scheduleRefresh();
842 else
843 this._refresh();
844 }
845
846 /**
847 * @return {!Network.NetworkTimeCalculator}
848 */
849 timeCalculator() {
850 return this._timeCalculator;
851 }
852
853 /**
854 * @return {!Network.NetworkTimeCalculator}
855 */
856 calculator() {
857 return this._calculator;
858 }
859
860 /**
861 * @param {!Network.NetworkTimeCalculator} x
862 */
863 setCalculator(x) {
864 if (!x || this._calculator === x)
865 return;
866
867 if (this._calculator !== x) {
868 this._calculator = x;
869 this._columns.setCalculator(this._calculator);
870 }
871 this._calculator.reset();
872
873 if (this._calculator.startAtZero)
874 this._columns.hideEventDividers();
875 else
876 this._columns.showEventDividers();
877
878 this._invalidateAllItems();
879 }
880
881 /**
882 * @param {!Common.Event} event
883 */
884 _loadEventFired(event) {
885 if (!this._recording)
886 return;
887
888 const time = /** @type {number} */ (event.data.loadTime);
889 if (time) {
890 this._mainRequestLoadTime = time;
Alexei Filippovfdcd8a62018-12-17 21:32:30891 this._columns.addEventDividers([time], 'network-load-divider');
Blink Reformat4c46d092018-04-07 15:32:37892 }
893 }
894
895 /**
896 * @param {!Common.Event} event
897 */
898 _domContentLoadedEventFired(event) {
899 if (!this._recording)
900 return;
901 const data = /** @type {number} */ (event.data);
902 if (data) {
903 this._mainRequestDOMContentLoadedTime = data;
Alexei Filippovfdcd8a62018-12-17 21:32:30904 this._columns.addEventDividers([data], 'network-dcl-divider');
Blink Reformat4c46d092018-04-07 15:32:37905 }
906 }
907
908 /**
909 * @override
910 */
911 wasShown() {
912 this._refreshIfNeeded();
913 this._columns.wasShown();
914 }
915
916 /**
917 * @override
918 */
919 willHide() {
920 this._columns.willHide();
921 }
922
923 /**
924 * @override
925 */
926 onResize() {
927 this._rowHeight = this._computeRowHeight();
928 }
929
930 /**
931 * @return {!Array<!Network.NetworkNode>}
932 */
933 flatNodesList() {
934 return this._dataGrid.rootNode().flatChildren();
935 }
936
937 stylesChanged() {
938 this._columns.scheduleRefresh();
939 }
940
941 _refresh() {
942 this._needsRefresh = false;
943
944 if (this._refreshRequestId) {
945 this.element.window().cancelAnimationFrame(this._refreshRequestId);
946 this._refreshRequestId = null;
947 }
948
949 this.removeAllNodeHighlights();
950
951 this._timeCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
952 this._durationCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
953 this._timeCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
954 this._durationCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
955
956 /** @type {!Map<!Network.NetworkNode, !Network.NetworkNode>} */
957 const nodesToInsert = new Map();
958 /** @type {!Array<!Network.NetworkNode>} */
959 const nodesToRefresh = [];
960
961 /** @type {!Set<!Network.NetworkRequestNode>} */
962 const staleNodes = new Set();
963
964 // While creating nodes it may add more entries into _staleRequests because redirect request nodes update the parent
965 // node so we loop until we have no more stale requests.
966 while (this._staleRequests.size) {
967 const request = this._staleRequests.firstValue();
968 this._staleRequests.delete(request);
969 let node = request[Network.NetworkLogView._networkNodeSymbol];
970 if (!node)
971 node = this._createNodeForRequest(request);
972 staleNodes.add(node);
973 }
974
975 for (const node of staleNodes) {
976 const isFilteredOut = !this._applyFilter(node);
977 if (isFilteredOut && node === this._hoveredNode)
978 this._setHoveredNode(null);
979
980 if (!isFilteredOut)
981 nodesToRefresh.push(node);
982 const request = node.request();
983 this._timeCalculator.updateBoundaries(request);
984 this._durationCalculator.updateBoundaries(request);
985 const newParent = this._parentNodeForInsert(node);
986 if (node[Network.NetworkLogView._isFilteredOutSymbol] === isFilteredOut && node.parent === newParent)
987 continue;
988 node[Network.NetworkLogView._isFilteredOutSymbol] = isFilteredOut;
989 const removeFromParent = node.parent && (isFilteredOut || node.parent !== newParent);
990 if (removeFromParent) {
991 let parent = node.parent;
992 parent.removeChild(node);
993 while (parent && !parent.hasChildren() && parent.dataGrid && parent.dataGrid.rootNode() !== parent) {
994 const grandparent = parent.parent;
995 grandparent.removeChild(parent);
996 parent = grandparent;
997 }
998 }
999
1000 if (!newParent || isFilteredOut)
1001 continue;
1002
1003 if (!newParent.dataGrid && !nodesToInsert.has(newParent)) {
1004 nodesToInsert.set(newParent, this._dataGrid.rootNode());
1005 nodesToRefresh.push(newParent);
1006 }
1007 nodesToInsert.set(node, newParent);
1008 }
1009
1010 for (const node of nodesToInsert.keys())
1011 nodesToInsert.get(node).appendChild(node);
1012
1013 for (const node of nodesToRefresh)
1014 node.refresh();
1015
1016 this._updateSummaryBar();
1017
1018 if (nodesToInsert.size)
1019 this._columns.sortByCurrentColumn();
1020
1021 this._dataGrid.updateInstantly();
1022 this._didRefreshForTest();
1023 }
1024
1025 _didRefreshForTest() {
1026 }
1027
1028 /**
1029 * @param {!Network.NetworkRequestNode} node
1030 * @return {?Network.NetworkNode}
1031 */
1032 _parentNodeForInsert(node) {
1033 if (!this._activeGroupLookup)
1034 return this._dataGrid.rootNode();
1035
1036 const groupNode = this._activeGroupLookup.groupNodeForRequest(node.request());
1037 if (!groupNode)
1038 return this._dataGrid.rootNode();
1039 return groupNode;
1040 }
1041
1042 _reset() {
1043 this.dispatchEventToListeners(Network.NetworkLogView.Events.RequestSelected, null);
1044
1045 this._setHoveredNode(null);
1046 this._columns.reset();
1047
1048 this._timeFilter = null;
1049 this._calculator.reset();
1050
1051 this._timeCalculator.setWindow(null);
1052 this.linkifier.reset();
1053 this.badgePool.reset();
1054
1055 if (this._activeGroupLookup)
1056 this._activeGroupLookup.reset();
1057 this._staleRequests.clear();
1058 this._resetSuggestionBuilder();
1059
1060 this._mainRequestLoadTime = -1;
1061 this._mainRequestDOMContentLoadedTime = -1;
1062
1063 this._dataGrid.rootNode().removeChildren();
1064 this._updateSummaryBar();
1065 this._dataGrid.setStickToBottom(true);
1066 this.scheduleRefresh();
1067 }
1068
1069 /**
1070 * @param {string} filterString
1071 */
1072 setTextFilterValue(filterString) {
1073 this._textFilterUI.setValue(filterString);
1074 this._dataURLFilterUI.setChecked(false);
1075 this._resourceCategoryFilterUI.reset();
1076 }
1077
1078 /**
1079 * @param {!SDK.NetworkRequest} request
1080 */
1081 _createNodeForRequest(request) {
1082 const node = new Network.NetworkRequestNode(this, request);
1083 request[Network.NetworkLogView._networkNodeSymbol] = node;
1084 node[Network.NetworkLogView._isFilteredOutSymbol] = true;
1085
1086 for (let redirect = request.redirectSource(); redirect; redirect = redirect.redirectSource())
1087 this._refreshRequest(redirect);
1088 return node;
1089 }
1090
1091 /**
1092 * @param {!Common.Event} event
1093 */
1094 _onRequestUpdated(event) {
1095 const request = /** @type {!SDK.NetworkRequest} */ (event.data);
1096 this._refreshRequest(request);
1097 }
1098
1099 /**
1100 * @param {!SDK.NetworkRequest} request
1101 */
1102 _refreshRequest(request) {
1103 Network.NetworkLogView._subdomains(request.domain)
1104 .forEach(
1105 this._suggestionBuilder.addItem.bind(this._suggestionBuilder, Network.NetworkLogView.FilterType.Domain));
1106 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Method, request.requestMethod);
1107 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MimeType, request.mimeType);
1108 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.Scheme, '' + request.scheme);
1109 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.StatusCode, '' + request.statusCode);
1110
1111 const priority = request.priority();
1112 if (priority) {
1113 this._suggestionBuilder.addItem(
1114 Network.NetworkLogView.FilterType.Priority, PerfUI.uiLabelForNetworkPriority(priority));
1115 }
1116
1117 if (request.mixedContentType !== Protocol.Security.MixedContentType.None) {
1118 this._suggestionBuilder.addItem(
1119 Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.All);
1120 }
1121
1122 if (request.mixedContentType === Protocol.Security.MixedContentType.OptionallyBlockable) {
1123 this._suggestionBuilder.addItem(
1124 Network.NetworkLogView.FilterType.MixedContent, Network.NetworkLogView.MixedContentFilterValues.Displayed);
1125 }
1126
1127 if (request.mixedContentType === Protocol.Security.MixedContentType.Blockable) {
1128 const suggestion = request.wasBlocked() ? Network.NetworkLogView.MixedContentFilterValues.Blocked :
1129 Network.NetworkLogView.MixedContentFilterValues.BlockOverridden;
1130 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.MixedContent, suggestion);
1131 }
1132
1133 const responseHeaders = request.responseHeaders;
1134 for (let i = 0, l = responseHeaders.length; i < l; ++i)
1135 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
1136 const cookies = request.responseCookies;
1137 for (let i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1138 const cookie = cookies[i];
1139 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
1140 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieName, cookie.name());
1141 this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.SetCookieValue, cookie.value());
1142 }
1143
1144 this._staleRequests.add(request);
1145 this.scheduleRefresh();
1146 }
1147
1148 /**
1149 * @return {number}
1150 */
1151 rowHeight() {
1152 return this._rowHeight;
1153 }
1154
1155 /**
1156 * @param {boolean} gridMode
1157 */
1158 switchViewMode(gridMode) {
1159 this._columns.switchViewMode(gridMode);
1160 }
1161
1162 /**
1163 * @param {!UI.ContextMenu} contextMenu
1164 * @param {!SDK.NetworkRequest} request
1165 */
1166 handleContextMenuForRequest(contextMenu, request) {
1167 contextMenu.appendApplicableItems(request);
1168 let copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
1169 const footerSection = copyMenu.footerSection();
1170 if (request) {
1171 copyMenu.defaultSection().appendItem(
1172 UI.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, request.contentURL()));
1173 if (request.requestHeadersText()) {
1174 copyMenu.defaultSection().appendItem(
1175 Common.UIString('Copy request headers'), Network.NetworkLogView._copyRequestHeaders.bind(null, request));
1176 }
1177
1178 if (request.responseHeadersText) {
1179 copyMenu.defaultSection().appendItem(
1180 Common.UIString('Copy response headers'), Network.NetworkLogView._copyResponseHeaders.bind(null, request));
1181 }
1182
1183 if (request.finished) {
1184 copyMenu.defaultSection().appendItem(
1185 Common.UIString('Copy response'), Network.NetworkLogView._copyResponse.bind(null, request));
1186 }
1187
Harley Libcf41f92018-09-10 18:01:131188 const disableIfBlob = request.isBlobRequest();
Blink Reformat4c46d092018-04-07 15:32:371189 if (Host.isWin()) {
1190 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131191 Common.UIString('Copy as PowerShell'), this._copyPowerShellCommand.bind(this, request), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371192 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131193 Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371194 footerSection.appendItem(
Harley Libcf41f92018-09-10 18:01:131195 Common.UIString('Copy as cURL (cmd)'), this._copyCurlCommand.bind(this, request, 'win'), disableIfBlob);
1196 footerSection.appendItem(
1197 Common.UIString('Copy as cURL (bash)'), this._copyCurlCommand.bind(this, request, 'unix'), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371198 footerSection.appendItem(Common.UIString('Copy all as PowerShell'), this._copyAllPowerShellCommand.bind(this));
1199 footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
1200 footerSection.appendItem(Common.UIString('Copy all as cURL (cmd)'), this._copyAllCurlCommand.bind(this, 'win'));
1201 footerSection.appendItem(
1202 Common.UIString('Copy all as cURL (bash)'), this._copyAllCurlCommand.bind(this, 'unix'));
1203 } else {
Harley Libcf41f92018-09-10 18:01:131204 footerSection.appendItem(
1205 Common.UIString('Copy as fetch'), this._copyFetchCall.bind(this, request), disableIfBlob);
1206 footerSection.appendItem(
1207 Common.UIString('Copy as cURL'), this._copyCurlCommand.bind(this, request, 'unix'), disableIfBlob);
Blink Reformat4c46d092018-04-07 15:32:371208 footerSection.appendItem(Common.UIString('Copy all as fetch'), this._copyAllFetchCall.bind(this));
1209 footerSection.appendItem(Common.UIString('Copy all as cURL'), this._copyAllCurlCommand.bind(this, 'unix'));
1210 }
1211 } else {
1212 copyMenu = contextMenu.clipboardSection().appendSubMenuItem(Common.UIString('Copy'));
1213 }
1214 footerSection.appendItem(Common.UIString('Copy all as HAR'), this._copyAll.bind(this));
1215
Joey Arhar0e1093c2019-05-21 00:34:221216 contextMenu.saveSection().appendItem(ls`Save all as HAR with content`, this.exportAll.bind(this));
Blink Reformat4c46d092018-04-07 15:32:371217
1218 contextMenu.editSection().appendItem(Common.UIString('Clear browser cache'), this._clearBrowserCache.bind(this));
1219 contextMenu.editSection().appendItem(
1220 Common.UIString('Clear browser cookies'), this._clearBrowserCookies.bind(this));
1221
1222 if (request) {
1223 const maxBlockedURLLength = 20;
1224 const manager = SDK.multitargetNetworkManager;
1225 let patterns = manager.blockedPatterns();
1226
Tim van der Lippeffa78622019-09-16 12:07:121227 /**
1228 * @param {string} url
1229 */
1230 function addBlockedURL(url) {
1231 patterns.push({enabled: true, url: url});
1232 manager.setBlockedPatterns(patterns);
1233 manager.setBlockingEnabled(true);
1234 UI.viewManager.showView('network.blocked-urls');
1235 }
1236
1237 /**
1238 * @param {string} url
1239 */
1240 function removeBlockedURL(url) {
1241 patterns = patterns.filter(pattern => pattern.url !== url);
1242 manager.setBlockedPatterns(patterns);
1243 UI.viewManager.showView('network.blocked-urls');
1244 }
1245
Blink Reformat4c46d092018-04-07 15:32:371246 const urlWithoutScheme = request.parsedURL.urlWithoutScheme();
1247 if (urlWithoutScheme && !patterns.find(pattern => pattern.url === urlWithoutScheme)) {
1248 contextMenu.debugSection().appendItem(
1249 Common.UIString('Block request URL'), addBlockedURL.bind(null, urlWithoutScheme));
1250 } else if (urlWithoutScheme) {
1251 const croppedURL = urlWithoutScheme.trimMiddle(maxBlockedURLLength);
1252 contextMenu.debugSection().appendItem(
1253 Common.UIString('Unblock %s', croppedURL), removeBlockedURL.bind(null, urlWithoutScheme));
1254 }
1255
1256 const domain = request.parsedURL.domain();
1257 if (domain && !patterns.find(pattern => pattern.url === domain)) {
1258 contextMenu.debugSection().appendItem(
1259 Common.UIString('Block request domain'), addBlockedURL.bind(null, domain));
1260 } else if (domain) {
1261 const croppedDomain = domain.trimMiddle(maxBlockedURLLength);
1262 contextMenu.debugSection().appendItem(
1263 Common.UIString('Unblock %s', croppedDomain), removeBlockedURL.bind(null, domain));
1264 }
1265
1266 if (SDK.NetworkManager.canReplayRequest(request)) {
1267 contextMenu.debugSection().appendItem(
1268 Common.UIString('Replay XHR'), SDK.NetworkManager.replayRequest.bind(null, request));
1269 }
Blink Reformat4c46d092018-04-07 15:32:371270 }
1271 }
1272
1273 _harRequests() {
Joey Arharb3d6de42019-04-23 21:26:171274 return SDK.networkLog.requests().filter(Network.NetworkLogView.HTTPRequestsFilter).filter(request => {
1275 return request.finished ||
1276 (request.resourceType() === Common.resourceTypes.WebSocket && request.responseReceivedTime);
1277 });
Blink Reformat4c46d092018-04-07 15:32:371278 }
1279
1280 async _copyAll() {
Pavel Feldman18d13562018-07-31 03:31:181281 const harArchive = {log: await SDK.HARLog.build(this._harRequests())};
Blink Reformat4c46d092018-04-07 15:32:371282 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1283 }
1284
1285 /**
1286 * @param {!SDK.NetworkRequest} request
1287 * @param {string} platform
1288 */
1289 async _copyCurlCommand(request, platform) {
1290 const command = await this._generateCurlCommand(request, platform);
1291 InspectorFrontendHost.copyText(command);
1292 }
1293
1294 /**
1295 * @param {string} platform
1296 */
1297 async _copyAllCurlCommand(platform) {
Harley Libcf41f92018-09-10 18:01:131298 const commands = await this._generateAllCurlCommand(SDK.networkLog.requests(), platform);
1299 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371300 }
1301
1302 /**
1303 * @param {!SDK.NetworkRequest} request
1304 * @param {string} platform
1305 */
1306 async _copyFetchCall(request, platform) {
1307 const command = await this._generateFetchCall(request);
1308 InspectorFrontendHost.copyText(command);
1309 }
1310
1311 async _copyAllFetchCall() {
Harley Libcf41f92018-09-10 18:01:131312 const commands = await this._generateAllFetchCall(SDK.networkLog.requests());
1313 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371314 }
1315
1316 /**
1317 * @param {!SDK.NetworkRequest} request
1318 */
1319 async _copyPowerShellCommand(request) {
1320 const command = await this._generatePowerShellCommand(request);
1321 InspectorFrontendHost.copyText(command);
1322 }
1323
1324 async _copyAllPowerShellCommand() {
Harley Libcf41f92018-09-10 18:01:131325 const commands = this._generateAllPowerShellCommand(SDK.networkLog.requests());
1326 InspectorFrontendHost.copyText(commands);
Blink Reformat4c46d092018-04-07 15:32:371327 }
1328
Joey Arhar0e1093c2019-05-21 00:34:221329 async exportAll() {
Blink Reformat4c46d092018-04-07 15:32:371330 const url = SDK.targetManager.mainTarget().inspectedURL();
1331 const parsedURL = url.asParsedURL();
1332 const filename = parsedURL ? parsedURL.host : 'network-log';
1333 const stream = new Bindings.FileOutputStream();
1334
1335 if (!await stream.open(filename + '.har'))
1336 return;
1337
1338 const progressIndicator = new UI.ProgressIndicator();
1339 this._progressBarContainer.appendChild(progressIndicator.element);
1340 await Network.HARWriter.write(stream, this._harRequests(), progressIndicator);
1341 progressIndicator.done();
1342 stream.close();
1343 }
1344
1345 _clearBrowserCache() {
1346 if (confirm(Common.UIString('Are you sure you want to clear browser cache?')))
1347 SDK.multitargetNetworkManager.clearBrowserCache();
1348 }
1349
1350 _clearBrowserCookies() {
1351 if (confirm(Common.UIString('Are you sure you want to clear browser cookies?')))
1352 SDK.multitargetNetworkManager.clearBrowserCookies();
1353 }
1354
1355 _removeAllHighlights() {
1356 this.removeAllNodeHighlights();
1357 for (let i = 0; i < this._highlightedSubstringChanges.length; ++i)
1358 UI.revertDomChanges(this._highlightedSubstringChanges[i]);
1359 this._highlightedSubstringChanges = [];
1360 }
1361
1362 /**
1363 * @param {!Network.NetworkRequestNode} node
1364 * @return {boolean}
1365 */
1366 _applyFilter(node) {
1367 const request = node.request();
1368 if (this._timeFilter && !this._timeFilter(request))
1369 return false;
1370 const categoryName = request.resourceType().category().title;
1371 if (!this._resourceCategoryFilterUI.accept(categoryName))
1372 return false;
Joey Arharba99d622019-02-01 19:10:481373 if (this._dataURLFilterUI.checked() && (request.parsedURL.isDataURL() || request.parsedURL.isBlobURL()))
Blink Reformat4c46d092018-04-07 15:32:371374 return false;
1375 if (request.statusText === 'Service Worker Fallback Required')
1376 return false;
1377 for (let i = 0; i < this._filters.length; ++i) {
1378 if (!this._filters[i](request))
1379 return false;
1380 }
1381 return true;
1382 }
1383
1384 /**
1385 * @param {string} query
1386 */
1387 _parseFilterQuery(query) {
1388 const descriptors = this._filterParser.parse(query);
1389 this._filters = descriptors.map(descriptor => {
1390 const key = descriptor.key;
1391 const text = descriptor.text || '';
1392 const regex = descriptor.regex;
1393 let filter;
1394 if (key) {
1395 const defaultText = (key + ':' + text).escapeForRegExp();
1396 filter = this._createSpecialFilter(/** @type {!Network.NetworkLogView.FilterType} */ (key), text) ||
1397 Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(defaultText, 'i'));
1398 } else if (descriptor.regex) {
1399 filter = Network.NetworkLogView._requestPathFilter.bind(null, /** @type {!RegExp} */ (regex));
1400 } else {
1401 filter = Network.NetworkLogView._requestPathFilter.bind(null, new RegExp(text.escapeForRegExp(), 'i'));
1402 }
1403 return descriptor.negative ? Network.NetworkLogView._negativeFilter.bind(null, filter) : filter;
1404 });
1405 }
1406
1407 /**
1408 * @param {!Network.NetworkLogView.FilterType} type
1409 * @param {string} value
1410 * @return {?Network.NetworkLogView.Filter}
1411 */
1412 _createSpecialFilter(type, value) {
1413 switch (type) {
1414 case Network.NetworkLogView.FilterType.Domain:
1415 return Network.NetworkLogView._createRequestDomainFilter(value);
1416
1417 case Network.NetworkLogView.FilterType.HasResponseHeader:
1418 return Network.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1419
1420 case Network.NetworkLogView.FilterType.Is:
1421 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.Running)
1422 return Network.NetworkLogView._runningRequestFilter;
1423 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.FromCache)
1424 return Network.NetworkLogView._fromCacheRequestFilter;
Joey Arhard183e7e2019-02-28 03:37:051425 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.ServiceWorkerIntercepted)
1426 return Network.NetworkLogView._interceptedByServiceWorkerFilter;
1427 if (value.toLowerCase() === Network.NetworkLogView.IsFilterType.ServiceWorkerInitiated)
1428 return Network.NetworkLogView._initiatedByServiceWorkerFilter;
Blink Reformat4c46d092018-04-07 15:32:371429 break;
1430
1431 case Network.NetworkLogView.FilterType.LargerThan:
1432 return this._createSizeFilter(value.toLowerCase());
1433
1434 case Network.NetworkLogView.FilterType.Method:
1435 return Network.NetworkLogView._requestMethodFilter.bind(null, value);
1436
1437 case Network.NetworkLogView.FilterType.MimeType:
1438 return Network.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1439
1440 case Network.NetworkLogView.FilterType.MixedContent:
1441 return Network.NetworkLogView._requestMixedContentFilter.bind(
1442 null, /** @type {!Network.NetworkLogView.MixedContentFilterValues} */ (value));
1443
1444 case Network.NetworkLogView.FilterType.Scheme:
1445 return Network.NetworkLogView._requestSchemeFilter.bind(null, value);
1446
1447 case Network.NetworkLogView.FilterType.SetCookieDomain:
1448 return Network.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1449
1450 case Network.NetworkLogView.FilterType.SetCookieName:
1451 return Network.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1452
1453 case Network.NetworkLogView.FilterType.SetCookieValue:
1454 return Network.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1455
1456 case Network.NetworkLogView.FilterType.Priority:
1457 return Network.NetworkLogView._requestPriorityFilter.bind(null, PerfUI.uiLabelToNetworkPriority(value));
1458
1459 case Network.NetworkLogView.FilterType.StatusCode:
1460 return Network.NetworkLogView._statusCodeFilter.bind(null, value);
1461 }
1462 return null;
1463 }
1464
1465 /**
1466 * @param {string} value
1467 * @return {?Network.NetworkLogView.Filter}
1468 */
1469 _createSizeFilter(value) {
1470 let multiplier = 1;
1471 if (value.endsWith('k')) {
1472 multiplier = 1024;
1473 value = value.substring(0, value.length - 1);
1474 } else if (value.endsWith('m')) {
1475 multiplier = 1024 * 1024;
1476 value = value.substring(0, value.length - 1);
1477 }
1478 const quantity = Number(value);
1479 if (isNaN(quantity))
1480 return null;
1481 return Network.NetworkLogView._requestSizeLargerThanFilter.bind(null, quantity * multiplier);
1482 }
1483
1484 _filterRequests() {
1485 this._removeAllHighlights();
1486 this._invalidateAllItems();
1487 }
1488
1489 /**
1490 * @param {!SDK.NetworkRequest} request
1491 * @return {?Network.NetworkRequestNode}
1492 */
1493 _reveal(request) {
1494 this.removeAllNodeHighlights();
1495 const node = request[Network.NetworkLogView._networkNodeSymbol];
1496 if (!node || !node.dataGrid)
1497 return null;
1498 node.reveal();
1499 return node;
1500 }
1501
1502 /**
1503 * @param {!SDK.NetworkRequest} request
1504 */
1505 revealAndHighlightRequest(request) {
1506 const node = this._reveal(request);
1507 if (node)
1508 this._highlightNode(node);
1509 }
1510
1511 /**
1512 * @param {!SDK.NetworkRequest} request
1513 */
1514 selectRequest(request) {
Eugene Ostroukhovb600f662018-05-09 00:18:141515 this.setTextFilterValue('');
Blink Reformat4c46d092018-04-07 15:32:371516 const node = this._reveal(request);
1517 if (node)
1518 node.select();
1519 }
1520
1521 removeAllNodeHighlights() {
1522 if (this._highlightedNode) {
1523 this._highlightedNode.element().classList.remove('highlighted-row');
1524 this._highlightedNode = null;
1525 }
1526 }
1527
1528 /**
1529 * @param {!Network.NetworkRequestNode} node
1530 */
1531 _highlightNode(node) {
1532 UI.runCSSAnimationOnce(node.element(), 'highlighted-row');
1533 this._highlightedNode = node;
1534 }
1535
1536 /**
Harley Libcf41f92018-09-10 18:01:131537 * @param {!Array<!SDK.NetworkRequest>} requests
1538 * @return {!Array<!SDK.NetworkRequest>}
1539 */
1540 _filterOutBlobRequests(requests) {
1541 return requests.filter(request => !request.isBlobRequest());
1542 }
1543
1544 /**
Blink Reformat4c46d092018-04-07 15:32:371545 * @param {!SDK.NetworkRequest} request
1546 * @return {!Promise<string>}
1547 */
1548 async _generateFetchCall(request) {
1549 const ignoredHeaders = {
1550 // Internal headers
1551 'method': 1,
1552 'path': 1,
1553 'scheme': 1,
1554 'version': 1,
1555
1556 // Unsafe headers
1557 // Keep this list synchronized with src/net/http/http_util.cc
1558 'accept-charset': 1,
1559 'accept-encoding': 1,
1560 'access-control-request-headers': 1,
1561 'access-control-request-method': 1,
1562 'connection': 1,
1563 'content-length': 1,
1564 'cookie': 1,
1565 'cookie2': 1,
1566 'date': 1,
1567 'dnt': 1,
1568 'expect': 1,
1569 'host': 1,
1570 'keep-alive': 1,
1571 'origin': 1,
1572 'referer': 1,
1573 'te': 1,
1574 'trailer': 1,
1575 'transfer-encoding': 1,
1576 'upgrade': 1,
1577 'via': 1,
1578 // TODO(phistuck) - remove this once crbug.com/571722 is fixed.
1579 'user-agent': 1
1580 };
1581
1582 const credentialHeaders = {'cookie': 1, 'authorization': 1};
1583
1584 const url = JSON.stringify(request.url());
1585
1586 const requestHeaders = request.requestHeaders();
1587 const headerData = requestHeaders.reduce((result, header) => {
1588 const name = header.name;
1589
1590 if (!ignoredHeaders[name.toLowerCase()] && !name.includes(':'))
1591 result.append(name, header.value);
1592
1593 return result;
1594 }, new Headers());
1595
1596 const headers = {};
1597 for (const headerArray of headerData)
PhistucK6ed0a3e2018-08-04 06:28:411598 headers[headerArray[0]] = headerArray[1];
Blink Reformat4c46d092018-04-07 15:32:371599
1600 const credentials =
1601 request.requestCookies || requestHeaders.some(({name}) => credentialHeaders[name.toLowerCase()]) ? 'include' :
1602 'omit';
1603
1604 const referrerHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'referer');
1605
1606 const referrer = referrerHeader ? referrerHeader.value : void 0;
1607
1608 const referrerPolicy = request.referrerPolicy() || void 0;
1609
1610 const requestBody = await request.requestFormData();
1611
1612 const fetchOptions = {
1613 credentials,
PhistucK6ed0a3e2018-08-04 06:28:411614 headers: Object.keys(headers).length ? headers : void 0,
Blink Reformat4c46d092018-04-07 15:32:371615 referrer,
1616 referrerPolicy,
1617 body: requestBody,
1618 method: request.requestMethod,
1619 mode: 'cors'
1620 };
1621
1622 const options = JSON.stringify(fetchOptions);
1623 return `fetch(${url}, ${options});`;
1624 }
1625
1626 /**
Harley Libcf41f92018-09-10 18:01:131627 * @param {!Array<!SDK.NetworkRequest>} requests
1628 * @return {!Promise<string>}
1629 */
1630 async _generateAllFetchCall(requests) {
1631 const nonBlobRequests = this._filterOutBlobRequests(requests);
1632 const commands = await Promise.all(nonBlobRequests.map(request => this._generateFetchCall(request)));
1633 return commands.join(' ;\n');
1634 }
1635
1636 /**
Blink Reformat4c46d092018-04-07 15:32:371637 * @param {!SDK.NetworkRequest} request
1638 * @param {string} platform
1639 * @return {!Promise<string>}
1640 */
1641 async _generateCurlCommand(request, platform) {
1642 let command = ['curl'];
1643 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1644 const ignoredHeaders = {'host': 1, 'method': 1, 'path': 1, 'scheme': 1, 'version': 1};
1645
1646 function escapeStringWin(str) {
1647 /* If there are no new line characters do not escape the " characters
1648 since it only uglifies the command.
1649
1650 Because cmd.exe parser and MS Crt arguments parsers use some of the
1651 same escape characters, they can interact with each other in
1652 horrible ways, the order of operations is critical.
1653
1654 Replace \ with \\ first because it is an escape character for certain
1655 conditions in both parsers.
1656
1657 Replace all " with \" to ensure the first parser does not remove it.
1658
1659 Then escape all characters we are not sure about with ^ to ensure it
1660 gets to MS Crt parser safely.
1661
1662 The % character is special because MS Crt parser will try and look for
1663 ENV variables and fill them in it's place. We cannot escape them with %
1664 and cannot escape them with ^ (because it's cmd.exe's escape not MS Crt
1665 parser); So we can get cmd.exe parser to escape the character after it,
1666 if it is followed by a valid beginning character of an ENV variable.
1667 This ensures we do not try and double escape another ^ if it was placed
1668 by the previous replace.
1669
1670 Lastly we replace new lines with ^ and TWO new lines because the first
1671 new line is there to enact the escape command the second is the character
1672 to escape (in this case new line).
1673 */
1674 const encapsChars = /[\r\n]/.test(str) ? '^"' : '"';
1675 return encapsChars +
1676 str.replace(/\\/g, '\\\\')
1677 .replace(/"/g, '\\"')
1678 .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
1679 .replace(/%(?=[a-zA-Z0-9_])/g, '%^')
1680 .replace(/\r\n|[\n\r]/g, '^\n\n') +
1681 encapsChars;
1682 }
1683
1684 /**
1685 * @param {string} str
1686 * @return {string}
1687 */
1688 function escapeStringPosix(str) {
1689 /**
1690 * @param {string} x
1691 * @return {string}
1692 */
1693 function escapeCharacter(x) {
Erik Luoaa676752018-08-21 05:52:221694 const code = x.charCodeAt(0);
Joey Arhar2d21f712019-05-20 21:07:121695 let hexString = code.toString(16);
1696 // Zero pad to four digits to comply with ANSI-C Quoting:
1697 // http://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
1698 while (hexString.length < 4)
1699 hexString = '0' + hexString;
1700
1701 return '\\u' + hexString;
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};