2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 2011 Google Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
33 * @implements {WebInspector.Searchable}
34 * @implements {WebInspector.TargetManager.Observer}
35 * @extends {WebInspector.VBox}
36 * @param {!WebInspector.FilterBar} filterBar
37 * @param {!WebInspector.Setting} coulmnsVisibilitySetting
39 WebInspector.NetworkLogView = function(filterBar, coulmnsVisibilitySetting)
41 WebInspector.VBox.call(this);
42 this.registerRequiredCSS("network/networkLogView.css");
43 this.registerRequiredCSS("ui/filter.css");
45 this._filterBar = filterBar;
46 this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
47 this._allowRequestSelection = false;
48 /** @type {!Map.<string, !WebInspector.NetworkDataGridNode>} */
49 this._nodesByRequestId = new Map();
50 /** @type {!Object.<string, boolean>} */
51 this._staleRequestIds = {};
53 this._mainRequestLoadTime = -1;
55 this._mainRequestDOMContentLoadedTime = -1;
56 this._matchedRequestCount = 0;
57 this._highlightedSubstringChanges = [];
59 /** @type {!Array.<!WebInspector.NetworkLogView.Filter>} */
62 this._currentMatchedRequestNode = null;
63 this._currentMatchedRequestIndex = -1;
65 this._createStatusbarButtons();
66 this._createStatusBarItems();
67 this._linkifier = new WebInspector.Linkifier();
69 this._allowPopover = true;
75 this._resetSuggestionBuilder();
76 this._initializeView();
77 this._toggleRecordButton(true);
79 WebInspector.targetManager.observeTargets(this);
80 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
81 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
82 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
84 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
85 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
86 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
87 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
90 WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
91 WebInspector.NetworkLogView._responseHeaderColumns = ["Cache-Control", "Connection", "Content-Encoding", "Content-Length", "ETag", "Keep-Alive", "Last-Modified", "Server", "Vary"];
92 WebInspector.NetworkLogView.defaultColumnsVisibility = {
93 method: true, status: true, scheme: false, domain: false, remoteAddress: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true, connectionId: false,
94 "Cache-Control": false, "Connection": false, "Content-Encoding": false, "Content-Length": false, "ETag": false, "Keep-Alive": false, "Last-Modified": false, "Server": false, "Vary": false
96 WebInspector.NetworkLogView._defaultRefreshDelay = 500;
99 WebInspector.NetworkLogView.FilterType = {
101 HasResponseHeader: "HasResponseHeader",
104 MimeType: "MimeType",
106 SetCookieDomain: "SetCookieDomain",
107 SetCookieName: "SetCookieName",
108 SetCookieValue: "SetCookieValue",
109 StatusCode: "StatusCode"
112 /** @enum {string} */
113 WebInspector.NetworkLogView.IsFilterType = {
117 /** @type {!Array.<string>} */
118 WebInspector.NetworkLogView._searchKeys = Object.values(WebInspector.NetworkLogView.FilterType);
120 /** @type {!Object.<string, string>} */
121 WebInspector.NetworkLogView._columnTitles = {
122 "name": WebInspector.UIString("Name"),
123 "method": WebInspector.UIString("Method"),
124 "status": WebInspector.UIString("Status"),
125 "scheme": WebInspector.UIString("Scheme"),
126 "domain": WebInspector.UIString("Domain"),
127 "remoteAddress": WebInspector.UIString("Remote Address"),
128 "type": WebInspector.UIString("Type"),
129 "initiator": WebInspector.UIString("Initiator"),
130 "cookies": WebInspector.UIString("Cookies"),
131 "setCookies": WebInspector.UIString("Set-Cookies"),
132 "size": WebInspector.UIString("Size"),
133 "time": WebInspector.UIString("Time"),
134 "connectionId": WebInspector.UIString("Connection Id"),
135 "timeline": WebInspector.UIString("Timeline"),
137 // Response header columns
138 "Cache-Control": WebInspector.UIString("Cache-Control"),
139 "Connection": WebInspector.UIString("Connection"),
140 "Content-Encoding": WebInspector.UIString("Content-Encoding"),
141 "Content-Length": WebInspector.UIString("Content-Length"),
142 "ETag": WebInspector.UIString("ETag"),
143 "Keep-Alive": WebInspector.UIString("Keep-Alive"),
144 "Last-Modified": WebInspector.UIString("Last-Modified"),
145 "Server": WebInspector.UIString("Server"),
146 "Vary": WebInspector.UIString("Vary")
149 WebInspector.NetworkLogView.prototype = {
151 * @param {!WebInspector.Target} target
153 targetAdded: function(target)
155 target.networkLog.requests.forEach(this._appendRequest.bind(this));
159 * @param {!WebInspector.Target} target
161 targetRemoved: function(target)
168 allowRequestSelection: function()
170 return this._allowRequestSelection;
173 _addFilters: function()
175 this._textFilterUI = new WebInspector.TextFilterUI();
176 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
177 this._filterBar.addFilter(this._textFilterUI);
180 for (var typeId in WebInspector.resourceTypes) {
181 var resourceType = WebInspector.resourceTypes[typeId];
182 if (resourceType === WebInspector.resourceTypes.TextTrack)
184 types.push({name: resourceType.name(), label: resourceType.categoryTitle()});
186 this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI(types, WebInspector.settings.networkResourceTypeFilters);
187 this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
188 this._filterBar.addFilter(this._resourceTypeFilterUI);
190 var dataURLSetting = WebInspector.settings.networkHideDataURL;
191 this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
192 this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
193 this._filterBar.addFilter(this._dataURLFilterUI);
196 _resetSuggestionBuilder: function()
198 this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkLogView._searchKeys);
199 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Is, WebInspector.NetworkLogView.IsFilterType.Running);
200 this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
204 * @param {!WebInspector.Event} event
206 _filterChanged: function(event)
208 this._removeAllNodeHighlights();
209 this._parseFilterQuery(this._textFilterUI.value());
210 this._filterRequests();
213 _initializeView: function()
215 this.element.id = "network-container";
217 this._createSortingFunctions();
218 this._createCalculators();
220 this._createTimelineGrid();
221 this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
223 this._updateRowsSize();
225 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
226 // Enable faster hint.
227 this._popoverHelper.setTimeout(250, 250);
229 this.switchViewMode(true);
233 * @return {!Array.<!Element>}
235 statusBarItems: function()
238 this._recordButton.element,
239 this._clearButton.element,
240 this._filterBar.filterButton().element,
241 this._largerRequestsButton.element,
242 this._preserveLogCheckbox.element,
243 this._disableCacheCheckbox.element,
244 this._progressBarContainer];
250 usesLargeRows: function()
252 return !!WebInspector.settings.resourcesLargeRows.get();
256 * @param {boolean} flag
258 setAllowPopover: function(flag)
260 this._allowPopover = flag;
264 * @return {!Array.<!Element>}
266 elementsToRestoreScrollPositionsFor: function()
268 if (!this._dataGrid) // Not initialized yet.
270 return [this._dataGrid.scrollContainer];
273 _createTimelineGrid: function()
275 this._timelineGrid = new WebInspector.TimelineGrid();
276 this._timelineGrid.element.classList.add("network-timeline-grid");
277 this._dataGrid.element.appendChild(this._timelineGrid.element);
280 _createTable: function()
285 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
286 title: WebInspector.NetworkLogView._columnTitles["name"],
294 title: WebInspector.NetworkLogView._columnTitles["method"],
301 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
302 title: WebInspector.NetworkLogView._columnTitles["status"],
309 title: WebInspector.NetworkLogView._columnTitles["scheme"],
316 title: WebInspector.NetworkLogView._columnTitles["domain"],
323 title: WebInspector.NetworkLogView._columnTitles["remoteAddress"],
326 align: WebInspector.DataGrid.Align.Right
331 title: WebInspector.NetworkLogView._columnTitles["type"],
338 title: WebInspector.NetworkLogView._columnTitles["initiator"],
345 title: WebInspector.NetworkLogView._columnTitles["cookies"],
348 align: WebInspector.DataGrid.Align.Right
353 title: WebInspector.NetworkLogView._columnTitles["setCookies"],
356 align: WebInspector.DataGrid.Align.Right
361 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
362 title: WebInspector.NetworkLogView._columnTitles["size"],
365 align: WebInspector.DataGrid.Align.Right
370 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
371 title: WebInspector.NetworkLogView._columnTitles["time"],
374 align: WebInspector.DataGrid.Align.Right
379 title: WebInspector.NetworkLogView._columnTitles["connectionId"],
384 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
385 for (var i = 0; i < responseHeaderColumns.length; ++i) {
386 var headerName = responseHeaderColumns[i];
389 title: WebInspector.NetworkLogView._columnTitles[headerName],
392 if (headerName === "Content-Length")
393 descriptor.align = WebInspector.DataGrid.Align.Right;
394 columns.push(descriptor);
399 titleDOMFragment: createDocumentFragment(),
400 title: WebInspector.NetworkLogView._columnTitles["timeline"],
403 sort: WebInspector.DataGrid.Order.Ascending
406 this._dataGrid = new WebInspector.SortableDataGrid(columns);
407 this._dataGrid.setStickToBottom(true);
408 this._updateColumns();
409 this._dataGrid.setName("networkLog");
410 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
411 this._dataGrid.element.classList.add("network-log-grid");
412 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
413 this._dataGrid.show(this.element);
415 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
416 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
417 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
419 this._patchTimelineHeader();
420 this._dataGrid.sortNodes(this._sortingFunctions.startTime, false);
424 * @param {string} title
425 * @param {string} subtitle
426 * @return {!DocumentFragment}
428 _makeHeaderFragment: function(title, subtitle)
430 var fragment = createDocumentFragment();
431 fragment.createTextChild(title);
432 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
433 subtitleDiv.createTextChild(subtitle);
437 _patchTimelineHeader: function()
439 var timelineSorting = createElement("select");
441 var option = createElement("option");
442 option.value = "startTime";
443 option.label = WebInspector.UIString("Timeline");
444 timelineSorting.appendChild(option);
446 option = createElement("option");
447 option.value = "startTime";
448 option.label = WebInspector.UIString("Start Time");
449 timelineSorting.appendChild(option);
451 option = createElement("option");
452 option.value = "responseTime";
453 option.label = WebInspector.UIString("Response Time");
454 timelineSorting.appendChild(option);
456 option = createElement("option");
457 option.value = "endTime";
458 option.label = WebInspector.UIString("End Time");
459 timelineSorting.appendChild(option);
461 option = createElement("option");
462 option.value = "duration";
463 option.label = WebInspector.UIString("Duration");
464 timelineSorting.appendChild(option);
466 option = createElement("option");
467 option.value = "latency";
468 option.label = WebInspector.UIString("Latency");
469 timelineSorting.appendChild(option);
471 var header = this._dataGrid.headerTableHeader("timeline");
472 header.replaceChild(timelineSorting, header.firstChild);
474 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
475 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
476 this._timelineSortSelector = timelineSorting;
479 _createSortingFunctions: function()
481 this._sortingFunctions = {};
482 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
483 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
484 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
485 this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
486 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
487 this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
488 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
489 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
490 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
491 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
492 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
493 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
494 this._sortingFunctions.connectionId = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "connectionId", false);
495 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
496 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
497 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
498 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
499 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
500 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
503 _createCalculators: function()
505 /** @type {!WebInspector.NetworkTransferTimeCalculator} */
506 this._timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
507 /** @type {!WebInspector.NetworkTransferDurationCalculator} */
508 this._durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
510 /** @type {!Object.<string, !WebInspector.NetworkTimeCalculator>} */
511 this._calculators = {};
512 this._calculators.timeline = this._timeCalculator;
513 this._calculators.startTime = this._timeCalculator;
514 this._calculators.endTime = this._timeCalculator;
515 this._calculators.responseTime = this._timeCalculator;
516 this._calculators.duration = this._durationCalculator;
517 this._calculators.latency = this._durationCalculator;
519 this._calculator = this._timeCalculator;
522 _sortItems: function()
524 this._removeAllNodeHighlights();
525 var columnIdentifier = this._dataGrid.sortColumnIdentifier();
526 if (columnIdentifier === "timeline") {
527 this._sortByTimeline();
530 var sortingFunction = this._sortingFunctions[columnIdentifier];
531 if (!sortingFunction)
534 this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
535 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
536 this._timelineSortSelector.selectedIndex = 0;
538 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
539 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
540 column: columnIdentifier,
541 sortOrder: this._dataGrid.sortOrder()
545 _sortByTimeline: function()
547 this._removeAllNodeHighlights();
548 var selectedIndex = this._timelineSortSelector.selectedIndex;
550 selectedIndex = 1; // Sort by start time by default.
551 var selectedOption = this._timelineSortSelector[selectedIndex];
552 var value = selectedOption.value;
554 this._setCalculator(this._calculators[value]);
555 var sortingFunction = this._sortingFunctions[value];
556 this._dataGrid.sortNodes(sortingFunction);
557 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
558 this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
561 _createStatusBarItems: function()
563 this._progressBarContainer = createElement("div");
564 this._progressBarContainer.className = "status-bar-item";
567 _updateSummaryBar: function()
569 var requestsNumber = this._nodesByRequestId.size;
571 if (!requestsNumber) {
572 if (this._summaryBarElement._isDisplayingWarning)
574 this._summaryBarElement._isDisplayingWarning = true;
575 this._summaryBarElement.removeChildren();
576 this._summaryBarElement.createChild("div", "warning-icon-small");
577 var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
578 this._summaryBarElement.createTextChild(text);
579 this._summaryBarElement.title = text;
582 delete this._summaryBarElement._isDisplayingWarning;
584 var transferSize = 0;
585 var selectedRequestsNumber = 0;
586 var selectedTransferSize = 0;
589 var nodes = this._nodesByRequestId.valuesArray();
590 for (var i = 0; i < nodes.length; ++i) {
591 var request = nodes[i].request();
592 var requestTransferSize = request.transferSize;
593 transferSize += requestTransferSize;
594 if (!nodes[i]._isFilteredOut) {
595 selectedRequestsNumber++;
596 selectedTransferSize += requestTransferSize;
598 if (request.url === request.target().resourceTreeModel.inspectedPageURL())
599 baseTime = request.startTime;
600 if (request.endTime > maxTime)
601 maxTime = request.endTime;
604 if (selectedRequestsNumber !== requestsNumber) {
605 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
606 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
608 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
609 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
611 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
612 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
613 Number.secondsToString(maxTime - baseTime),
614 Number.secondsToString(this._mainRequestLoadTime - baseTime),
615 Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
617 this._summaryBarElement.textContent = text;
618 this._summaryBarElement.title = text;
621 _scheduleRefresh: function()
623 if (this._needsRefresh)
626 this._needsRefresh = true;
628 if (this.isShowing() && !this._refreshTimeout)
629 this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
632 _updateDividersIfNeeded: function()
634 var timelineOffset = this._dataGrid.columnOffset("timeline");
635 // Position timline grid location.
637 this._timelineGrid.element.style.left = timelineOffset + "px";
639 var calculator = this.calculator();
641 if (!this.isShowing()) {
642 this._scheduleRefresh();
645 calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
646 proceed = this._timelineGrid.updateDividers(calculator);
651 if (calculator.startAtZero) {
652 // If our current sorting method starts at zero, that means it shows all
653 // requests starting at the same point, and so onLoad event and DOMContent
654 // event lines really wouldn't make much sense here, so don't render them.
658 this._timelineGrid.removeEventDividers();
659 if (this._mainRequestLoadTime !== -1) {
660 var percent = calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
662 var loadDivider = createElement("div");
663 loadDivider.className = "network-event-divider network-red-divider";
665 var loadDividerPadding = createElement("div");
666 loadDividerPadding.className = "network-event-divider-padding";
667 loadDividerPadding.title = WebInspector.UIString("Load event");
668 loadDividerPadding.appendChild(loadDivider);
669 loadDividerPadding.style.left = percent + "%";
670 this._timelineGrid.addEventDivider(loadDividerPadding);
673 if (this._mainRequestDOMContentLoadedTime !== -1) {
674 var percent = calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
676 var domContentLoadedDivider = createElement("div");
677 domContentLoadedDivider.className = "network-event-divider network-blue-divider";
679 var domContentLoadedDividerPadding = createElement("div");
680 domContentLoadedDividerPadding.className = "network-event-divider-padding";
681 domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event");
682 domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
683 domContentLoadedDividerPadding.style.left = percent + "%";
684 this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
688 _refreshIfNeeded: function()
690 if (this._needsRefresh)
694 _invalidateAllItems: function()
696 var requestIds = this._nodesByRequestId.keysArray();
697 for (var i = 0; i < requestIds.length; ++i)
698 this._staleRequestIds[requestIds[i]] = true;
702 * @return {!WebInspector.NetworkTimeCalculator}
704 calculator: function()
706 return this._calculator;
710 * @param {!WebInspector.NetworkTimeCalculator} x
712 _setCalculator: function(x)
714 if (!x || this._calculator === x)
717 this._calculator = x;
718 this._calculator.reset();
720 if (this._calculator.startAtZero)
721 this._timelineGrid.hideEventDividers();
723 this._timelineGrid.showEventDividers();
725 this._invalidateAllItems();
729 _createStatusbarButtons: function()
731 this._recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
732 this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
734 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
735 this._clearButton.addEventListener("click", this._reset, this);
737 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
738 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
739 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
741 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
742 this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
744 this._disableCacheCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Disable cache"));
745 WebInspector.SettingsUI.bindCheckbox(this._disableCacheCheckbox.inputElement, WebInspector.settings.cacheDisabled);
746 this._disableCacheCheckbox.element.title = WebInspector.UIString("Disable cache (while DevTools is open).");
750 * @param {!WebInspector.Event} event
752 _loadEventFired: function(event)
754 if (!this._recordButton.toggled)
757 var data = /** @type {number} */ (event.data);
758 this._mainRequestLoadTime = data || -1;
759 // Schedule refresh to update boundaries and draw the new line.
760 this._scheduleRefresh();
764 * @param {!WebInspector.Event} event
766 _domContentLoadedEventFired: function(event)
768 if (!this._recordButton.toggled)
770 var data = /** @type {number} */ (event.data);
771 this._mainRequestDOMContentLoadedTime = data || -1;
772 // Schedule refresh to update boundaries and draw the new line.
773 this._scheduleRefresh();
778 this._refreshIfNeeded();
783 this._popoverHelper.hidePopover();
788 this._needsRefresh = false;
789 if (this._refreshTimeout) {
790 clearTimeout(this._refreshTimeout);
791 delete this._refreshTimeout;
794 this._removeAllNodeHighlights();
795 var boundariesChanged = false;
796 var calculator = this.calculator();
797 if (calculator.updateBoundariesForEventTime) {
798 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
799 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
802 var dataGrid = this._dataGrid;
803 var rootNode = dataGrid.rootNode();
804 var nodesToInsert = [];
805 for (var requestId in this._staleRequestIds) {
806 var node = this._nodesByRequestId.get(requestId);
809 if (!node._isFilteredOut)
810 rootNode.removeChild(node);
811 node._isFilteredOut = !this._applyFilter(node);
812 if (!node._isFilteredOut)
813 nodesToInsert.push(node);
816 for (var i = 0; i < nodesToInsert.length; ++i) {
817 var node = nodesToInsert[i];
818 var request = node.request();
820 dataGrid.insertChild(node);
821 node._isMatchingSearchQuery = this._matchRequest(request);
822 if (calculator.updateBoundaries(request))
823 boundariesChanged = true;
826 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
828 if (boundariesChanged) {
829 // The boundaries changed, so all item graphs are stale.
830 this._updateDividersIfNeeded();
831 var nodes = this._nodesByRequestId.valuesArray();
832 for (var i = 0; i < nodes.length; ++i)
833 nodes[i].refreshGraph();
836 this._staleRequestIds = {};
837 this._updateSummaryBar();
840 _onRecordButtonClicked: function()
842 if (!this._recordButton.toggled)
844 this._toggleRecordButton(!this._recordButton.toggled);
848 * @param {boolean} toggled
850 _toggleRecordButton: function(toggled)
852 this._recordButton.toggled = toggled;
853 this._recordButton.title = toggled ? WebInspector.UIString("Stop Recording Network Log") : WebInspector.UIString("Record Network Log");
858 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
860 this._clearSearchMatchedList();
861 if (this._popoverHelper)
862 this._popoverHelper.hidePopover();
864 if (this._calculator)
865 this._calculator.reset();
867 var nodes = this._nodesByRequestId.valuesArray();
868 for (var i = 0; i < nodes.length; ++i)
871 this._nodesByRequestId.clear();
872 this._staleRequestIds = {};
873 this._resetSuggestionBuilder();
875 if (this._dataGrid) {
876 this._dataGrid.rootNode().removeChildren();
877 this._updateDividersIfNeeded();
878 this._updateSummaryBar();
881 this._mainRequestLoadTime = -1;
882 this._mainRequestDOMContentLoadedTime = -1;
886 * @param {!WebInspector.Event} event
888 _onRequestStarted: function(event)
890 if (this._recordButton.toggled) {
891 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
892 this._appendRequest(request);
897 * @param {!WebInspector.NetworkRequest} request
899 _appendRequest: function(request)
901 var node = new WebInspector.NetworkDataGridNode(this, request);
903 // In case of redirect request id is reassigned to a redirected
904 // request and we need to update _nodesByRequestId and search results.
905 var originalRequestNode = this._nodesByRequestId.get(request.requestId);
906 if (originalRequestNode)
907 this._nodesByRequestId.set(originalRequestNode.request().requestId, originalRequestNode);
908 this._nodesByRequestId.set(request.requestId, node);
910 // Pull all the redirects of the main request upon commit load.
911 if (request.redirects) {
912 for (var i = 0; i < request.redirects.length; ++i)
913 this._refreshRequest(request.redirects[i]);
916 this._refreshRequest(request);
920 * @param {!WebInspector.Event} event
922 _onRequestUpdated: function(event)
924 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
925 this._refreshRequest(request);
929 * @param {!WebInspector.NetworkRequest} request
931 _refreshRequest: function(request)
933 if (!this._nodesByRequestId.get(request.requestId))
936 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Domain, request.domain);
937 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Method, request.requestMethod);
938 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MimeType, request.mimeType);
939 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Scheme, "" + request.scheme);
940 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.StatusCode, "" + request.statusCode);
942 var responseHeaders = request.responseHeaders;
943 for (var i = 0, l = responseHeaders.length; i < l; ++i)
944 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
945 var cookies = request.responseCookies;
946 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
947 var cookie = cookies[i];
948 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
949 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieName, cookie.name());
950 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieValue, cookie.value());
953 this._staleRequestIds[request.requestId] = true;
954 this._scheduleRefresh();
958 * @param {!WebInspector.Event} event
960 _willReloadPage: function(event)
962 this._recordButton.toggled = true;
963 if (!this._preserveLogCheckbox.checked())
968 * @param {!WebInspector.Event} event
970 _mainFrameNavigated: function(event)
972 if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
975 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
976 var loaderId = frame.loaderId;
978 // Pick provisional load requests.
979 var requestsToPick = [];
980 var requests = frame.target().networkLog.requests;
981 for (var i = 0; i < requests.length; ++i) {
982 var request = requests[i];
983 if (request.loaderId === loaderId)
984 requestsToPick.push(request);
989 for (var i = 0; i < requestsToPick.length; ++i)
990 this._appendRequest(requestsToPick[i]);
994 * @param {boolean} detailed
996 switchViewMode: function(detailed)
998 if (this._detailedMode === detailed)
1000 this._detailedMode = detailed;
1003 if (this._dataGrid.selectedNode)
1004 this._dataGrid.selectedNode.selected = false;
1006 this._removeAllNodeHighlights();
1007 this._popoverHelper.hidePopover();
1010 this.element.classList.toggle("brief-mode", !detailed);
1011 this._updateColumns();
1014 _toggleLargerRequests: function()
1016 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
1017 this._updateRowsSize();
1023 rowHeight: function()
1025 return this._rowHeight;
1028 _updateRowsSize: function()
1030 var largeRows = this.usesLargeRows();
1031 this._largerRequestsButton.toggled = largeRows;
1032 this._rowHeight = largeRows ? 41 : 21;
1033 this._largerRequestsButton.title = WebInspector.UIString(largeRows ? "Use small resource rows." : "Use large resource rows.");
1034 this._dataGrid.element.classList.toggle("small", !largeRows);
1035 this._timelineGrid.element.classList.toggle("small", !largeRows);
1036 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: largeRows });
1040 * @param {!Element} element
1041 * @param {!Event} event
1042 * @return {!Element|!AnchorBox|undefined}
1044 _getPopoverAnchor: function(element, event)
1046 if (!this._allowPopover)
1048 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
1049 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
1051 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
1052 if (anchor && anchor.request) {
1053 var request = /** @type {!WebInspector.NetworkRequest} */ (anchor.request);
1054 var initiator = anchor.request.initiator();
1055 if (initiator && (initiator.stackTrace || initiator.asyncStackTrace))
1061 * @param {!Element} anchor
1062 * @param {!WebInspector.Popover} popover
1064 _showPopover: function(anchor, popover)
1067 if (anchor.classList.contains("network-script-initiated")) {
1068 content = this._generateScriptInitiatedPopoverContent(anchor.request);
1069 popover.setCanShrink(true);
1071 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
1072 popover.setCanShrink(false);
1074 popover.show(content, anchor);
1077 _onHidePopover: function()
1079 this._linkifier.reset();
1083 * @param {!WebInspector.NetworkRequest} request
1084 * @return {!Element}
1086 _generateScriptInitiatedPopoverContent: function(request)
1088 var framesTable = createElementWithClass("table", "network-stack-trace");
1091 * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
1092 * @this {WebInspector.NetworkLogView}
1094 function appendStackTrace(stackTrace)
1096 for (var i = 0; i < stackTrace.length; ++i) {
1097 var stackFrame = stackTrace[i];
1098 var row = createElement("tr");
1099 row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
1100 row.createChild("td").textContent = " @ ";
1101 row.createChild("td").appendChild(this._linkifier.linkifyConsoleCallFrame(request.target(), stackFrame));
1102 framesTable.appendChild(row);
1106 // Initiator is not null, checked in _getPopoverAnchor.
1107 var initiator = /** @type {!NetworkAgent.Initiator} */ (request.initiator());
1108 if (initiator.stackTrace)
1109 appendStackTrace.call(this, initiator.stackTrace);
1111 var asyncStackTrace = initiator.asyncStackTrace;
1112 while (asyncStackTrace) {
1113 var callFrames = asyncStackTrace.callFrames;
1114 if (!callFrames || !callFrames.length)
1116 var row = framesTable.createChild("tr");
1117 row.createChild("td", "network-async-trace-description").textContent = WebInspector.asyncStackTraceLabel(asyncStackTrace.description);
1118 row.createChild("td");
1119 row.createChild("td");
1120 appendStackTrace.call(this, callFrames);
1121 asyncStackTrace = asyncStackTrace.asyncStackTrace;
1127 _updateColumns: function()
1129 var detailedMode = !!this._detailedMode;
1130 var visibleColumns = {"name": true};
1132 visibleColumns["timeline"] = true;
1133 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1134 for (var columnIdentifier in columnsVisibility)
1135 visibleColumns[columnIdentifier] = columnsVisibility[columnIdentifier];
1138 this._dataGrid.setColumnsVisiblity(visibleColumns);
1142 * @param {string} columnIdentifier
1144 _toggleColumnVisibility: function(columnIdentifier)
1146 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1147 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
1148 this._coulmnsVisibilitySetting.set(columnsVisibility);
1150 this._updateColumns();
1154 * @return {!Array.<string>}
1156 _getConfigurableColumnIDs: function()
1158 if (this._configurableColumnIDs)
1159 return this._configurableColumnIDs;
1161 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1162 function compare(id1, id2)
1164 return columnTitles[id1].compareTo(columnTitles[id2]);
1167 var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
1168 this._configurableColumnIDs = columnIDs.sort(compare);
1169 return this._configurableColumnIDs;
1173 * @param {!Event} event
1175 _contextMenu: function(event)
1177 var contextMenu = new WebInspector.ContextMenu(event);
1179 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
1180 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1181 var columnIDs = this._getConfigurableColumnIDs();
1182 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1183 for (var i = 0; i < columnIDs.length; ++i) {
1184 var columnIdentifier = columnIDs[i];
1185 contextMenu.appendCheckboxItem(columnTitles[columnIdentifier], this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1191 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1192 var request = gridNode && gridNode.request();
1195 * @param {string} url
1197 function openResourceInNewTab(url)
1199 InspectorFrontendHost.openInNewTab(url);
1203 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), openResourceInNewTab.bind(null, request.url));
1204 contextMenu.appendSeparator();
1205 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1206 if (request.requestHeadersText())
1207 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1208 if (request.responseHeadersText)
1209 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1210 if (request.finished)
1211 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response" : "Copy Response"), this._copyResponse.bind(this, request));
1212 contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1214 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1216 contextMenu.appendSeparator();
1217 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1219 contextMenu.appendSeparator();
1220 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1221 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1223 if (request && request.resourceType() === WebInspector.resourceTypes.XHR) {
1224 contextMenu.appendSeparator();
1225 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), request.replayXHR.bind(request));
1226 contextMenu.appendSeparator();
1232 _harRequests: function()
1234 var requests = this._nodesByRequestId.valuesArray().map(function(node) { return node.request(); });
1235 var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1236 httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1237 return httpRequests.filter(WebInspector.NetworkLogView.NonDevToolsRequestsFilter);
1240 _copyAll: function()
1243 log: (new WebInspector.HARLog(this._harRequests())).build()
1245 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1249 * @param {!WebInspector.NetworkRequest} request
1251 _copyLocation: function(request)
1253 InspectorFrontendHost.copyText(request.url);
1257 * @param {!WebInspector.NetworkRequest} request
1259 _copyRequestHeaders: function(request)
1261 InspectorFrontendHost.copyText(request.requestHeadersText());
1265 * @param {!WebInspector.NetworkRequest} request
1267 _copyResponse: function(request)
1270 * @param {?string} content
1272 function callback(content)
1274 if (request.contentEncoded)
1275 content = request.asDataURL();
1276 InspectorFrontendHost.copyText(content || "");
1278 request.requestContent(callback);
1282 * @param {!WebInspector.NetworkRequest} request
1284 _copyResponseHeaders: function(request)
1286 InspectorFrontendHost.copyText(request.responseHeadersText);
1290 * @param {!WebInspector.NetworkRequest} request
1292 _copyCurlCommand: function(request)
1294 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1297 _exportAll: function()
1299 var filename = WebInspector.targetManager.inspectedPageDomain() + ".har";
1300 var stream = new WebInspector.FileOutputStream();
1301 stream.open(filename, openCallback.bind(this));
1304 * @param {boolean} accepted
1305 * @this {WebInspector.NetworkLogView}
1307 function openCallback(accepted)
1311 var progressIndicator = new WebInspector.ProgressIndicator();
1312 this._progressBarContainer.appendChild(progressIndicator.element);
1313 var harWriter = new WebInspector.HARWriter();
1314 harWriter.write(stream, this._harRequests(), progressIndicator);
1318 _clearBrowserCache: function()
1320 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1321 NetworkAgent.clearBrowserCache();
1324 _clearBrowserCookies: function()
1326 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1327 NetworkAgent.clearBrowserCookies();
1331 * @param {!WebInspector.NetworkRequest} request
1334 _matchRequest: function(request)
1336 var re = this._searchRegExp;
1339 return re.test(request.name()) || re.test(request.path());
1342 _clearSearchMatchedList: function()
1344 this._matchedRequestCount = -1;
1345 this._currentMatchedRequestNode = null;
1346 this._removeAllHighlights();
1349 _removeAllHighlights: function()
1351 this._removeAllNodeHighlights();
1352 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1353 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1354 this._highlightedSubstringChanges = [];
1359 * @param {boolean} reveal
1361 _highlightNthMatchedRequestForSearch: function(n, reveal)
1363 this._removeAllHighlights();
1365 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1366 var nodes = this._dataGrid.rootNode().children;
1369 for (var i = 0; i < nodes.length; ++i) {
1370 if (nodes[i]._isMatchingSearchQuery) {
1371 if (matchCount === n) {
1379 this._currentMatchedRequestNode = null;
1383 var request = node.request();
1384 var regExp = this._searchRegExp;
1385 var nameMatched = request.name().match(regExp);
1386 var pathMatched = request.path().match(regExp);
1387 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1388 this._toggleLargerRequests();
1390 WebInspector.Revealer.reveal(request);
1391 var highlightedSubstringChanges = node.highlightMatchedSubstring(regExp);
1392 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1394 this._currentMatchedRequestNode = node;
1395 this._currentMatchedRequestIndex = n;
1396 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
1400 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
1401 * @param {boolean} shouldJump
1402 * @param {boolean=} jumpBackwards
1404 performSearch: function(searchConfig, shouldJump, jumpBackwards)
1406 var query = searchConfig.query;
1407 var currentMatchedRequestNode = this._currentMatchedRequestNode;
1408 this._clearSearchMatchedList();
1409 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1411 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1412 var nodes = this._dataGrid.rootNode().children;
1413 for (var i = 0; i < nodes.length; ++i)
1414 nodes[i]._isMatchingSearchQuery = this._matchRequest(nodes[i].request());
1415 var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
1416 if (!newMatchedRequestIndex && jumpBackwards)
1417 newMatchedRequestIndex = this._matchedRequestCount - 1;
1418 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
1424 supportsCaseSensitiveSearch: function()
1432 supportsRegexSearch: function()
1438 * @param {?WebInspector.NetworkDataGridNode} node
1441 _updateMatchCountAndFindMatchIndex: function(node)
1443 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1444 var nodes = this._dataGrid.rootNode().children;
1447 for (var i = 0; i < nodes.length; ++i) {
1448 if (!nodes[i]._isMatchingSearchQuery)
1450 if (node === nodes[i])
1451 matchIndex = matchCount;
1454 if (this._matchedRequestCount !== matchCount) {
1455 this._matchedRequestCount = matchCount;
1456 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
1462 * @param {number} index
1465 _normalizeSearchResultIndex: function(index)
1467 return (index + this._matchedRequestCount) % this._matchedRequestCount;
1471 * @param {!WebInspector.NetworkDataGridNode} node
1474 _applyFilter: function(node)
1476 var request = node.request();
1477 var resourceType = request.resourceType();
1478 if (resourceType === WebInspector.resourceTypes.TextTrack)
1479 resourceType = WebInspector.resourceTypes.Other;
1480 if (!this._resourceTypeFilterUI.accept(resourceType.name()))
1482 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1484 for (var i = 0; i < this._filters.length; ++i) {
1485 if (!this._filters[i](request))
1492 * @param {string} query
1494 _parseFilterQuery: function(query)
1496 var parsedQuery = this._suggestionBuilder.parseQuery(query);
1497 this._filters = parsedQuery.text.map(this._createTextFilter);
1498 var filters = parsedQuery.filters;
1499 var n = parsedQuery.filters.length;
1500 for (var i = 0; i < n; ++i) {
1501 var filter = parsedQuery.filters[i];
1502 var filterType = /** @type {!WebInspector.NetworkLogView.FilterType} */ (filter.type);
1503 this._filters.push(this._createFilter(filterType, filter.data, filter.negative));
1508 * @param {string} text
1509 * @return {!WebInspector.NetworkLogView.Filter}
1511 _createTextFilter: function(text)
1513 var regexp = new RegExp(text.escapeForRegExp(), "i");
1514 return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1518 * @param {!WebInspector.NetworkLogView.FilterType} type
1519 * @param {string} value
1520 * @param {boolean} negative
1521 * @return {!WebInspector.NetworkLogView.Filter}
1523 _createFilter: function(type, value, negative)
1525 var filter = this._createSpecialFilter(type, value);
1527 return this._createTextFilter((negative ? "-" : "") + type + ":" + value);
1529 return WebInspector.NetworkLogView._negativeFilter.bind(null, filter);
1534 * @param {!WebInspector.NetworkLogView.FilterType} type
1535 * @param {string} value
1536 * @return {?WebInspector.NetworkLogView.Filter}
1538 _createSpecialFilter: function(type, value)
1541 case WebInspector.NetworkLogView.FilterType.Domain:
1542 return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1544 case WebInspector.NetworkLogView.FilterType.HasResponseHeader:
1545 return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1547 case WebInspector.NetworkLogView.FilterType.Is:
1548 if (value.toLowerCase() === WebInspector.NetworkLogView.IsFilterType.Running)
1549 return WebInspector.NetworkLogView._runningRequestFilter;
1552 case WebInspector.NetworkLogView.FilterType.Method:
1553 return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1555 case WebInspector.NetworkLogView.FilterType.MimeType:
1556 return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1558 case WebInspector.NetworkLogView.FilterType.Scheme:
1559 return WebInspector.NetworkLogView._requestSchemeFilter.bind(null, value);
1561 case WebInspector.NetworkLogView.FilterType.SetCookieDomain:
1562 return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1564 case WebInspector.NetworkLogView.FilterType.SetCookieName:
1565 return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1567 case WebInspector.NetworkLogView.FilterType.SetCookieValue:
1568 return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1570 case WebInspector.NetworkLogView.FilterType.StatusCode:
1571 return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1576 _filterRequests: function()
1578 this._removeAllHighlights();
1579 this._invalidateAllItems();
1583 jumpToPreviousSearchResult: function()
1585 if (!this._matchedRequestCount)
1587 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1588 this._highlightNthMatchedRequestForSearch(index, true);
1591 jumpToNextSearchResult: function()
1593 if (!this._matchedRequestCount)
1595 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1596 this._highlightNthMatchedRequestForSearch(index, true);
1599 searchCanceled: function()
1601 delete this._searchRegExp;
1602 this._clearSearchMatchedList();
1603 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1607 * @param {!WebInspector.NetworkRequest} request
1609 revealAndHighlightRequest: function(request)
1611 this._removeAllNodeHighlights();
1613 var node = this._nodesByRequestId.get(request.requestId);
1616 this._highlightNode(node);
1620 _removeAllNodeHighlights: function()
1622 if (this._highlightedNode) {
1623 this._highlightedNode.element().classList.remove("highlighted-row");
1624 delete this._highlightedNode;
1629 * @param {!WebInspector.NetworkDataGridNode} node
1631 _highlightNode: function(node)
1633 WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
1634 this._highlightedNode = node;
1638 * @param {!WebInspector.NetworkRequest} request
1641 _generateCurlCommand: function(request)
1643 var command = ["curl"];
1644 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1645 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1647 function escapeStringWin(str)
1649 /* Replace quote by double quote (but not by \") because it is
1650 recognized by both cmd.exe and MS Crt arguments parser.
1652 Replace % by "%" because it could be expanded to an environment
1653 variable value. So %% becomes "%""%". Even if an env variable ""
1654 (2 doublequotes) is declared, the cmd.exe will not
1655 substitute it with its value.
1657 Replace each backslash with double backslash to make sure
1658 MS Crt arguments parser won't collapse them.
1660 Replace new line outside of quotes since cmd.exe doesn't let
1663 return "\"" + str.replace(/"/g, "\"\"")
1664 .replace(/%/g, "\"%\"")
1665 .replace(/\\/g, "\\\\")
1666 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1669 function escapeStringPosix(str)
1671 function escapeCharacter(x)
1673 var code = x.charCodeAt(0);
1675 // Add leading zero when needed to not care about the next character.
1676 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1678 code = code.toString(16);
1679 return "\\u" + ("0000" + code).substr(code.length, 4);
1682 if (/[^\x20-\x7E]|\'/.test(str)) {
1683 // Use ANSI-C quoting syntax.
1684 return "$\'" + str.replace(/\\/g, "\\\\")
1685 .replace(/\'/g, "\\\'")
1686 .replace(/\n/g, "\\n")
1687 .replace(/\r/g, "\\r")
1688 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1690 // Use single quote syntax.
1691 return "'" + str + "'";
1695 // cURL command expected to run on the same platform that DevTools run
1696 // (it may be different from the inspected page platform).
1697 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1699 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1701 var inferredMethod = "GET";
1703 var requestContentType = request.requestContentType();
1704 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1705 data.push("--data");
1706 data.push(escapeString(request.requestFormData));
1707 ignoredHeaders["content-length"] = true;
1708 inferredMethod = "POST";
1709 } else if (request.requestFormData) {
1710 data.push("--data-binary");
1711 data.push(escapeString(request.requestFormData));
1712 ignoredHeaders["content-length"] = true;
1713 inferredMethod = "POST";
1716 if (request.requestMethod !== inferredMethod) {
1718 command.push(request.requestMethod);
1721 var requestHeaders = request.requestHeaders();
1722 for (var i = 0; i < requestHeaders.length; i++) {
1723 var header = requestHeaders[i];
1724 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1725 if (name.toLowerCase() in ignoredHeaders)
1728 command.push(escapeString(name + ": " + header.value));
1730 command = command.concat(data);
1731 command.push("--compressed");
1732 return command.join(" ");
1735 __proto__: WebInspector.VBox.prototype
1738 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1739 WebInspector.NetworkLogView.Filter;
1742 * @param {!WebInspector.NetworkLogView.Filter} filter
1743 * @param {!WebInspector.NetworkRequest} request
1746 WebInspector.NetworkLogView._negativeFilter = function(filter, request)
1748 return !filter(request);
1752 * @param {!RegExp} regex
1753 * @param {!WebInspector.NetworkRequest} request
1756 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1758 return regex.test(request.name()) || regex.test(request.path());
1762 * @param {string} value
1763 * @param {!WebInspector.NetworkRequest} request
1766 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1768 return request.domain === value;
1772 * @param {!WebInspector.NetworkRequest} request
1775 WebInspector.NetworkLogView._runningRequestFilter = function(request)
1777 return !request.finished;
1781 * @param {string} value
1782 * @param {!WebInspector.NetworkRequest} request
1785 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1787 return request.responseHeaderValue(value) !== undefined;
1791 * @param {string} value
1792 * @param {!WebInspector.NetworkRequest} request
1795 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1797 return request.requestMethod === value;
1801 * @param {string} value
1802 * @param {!WebInspector.NetworkRequest} request
1805 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1807 return request.mimeType === value;
1811 * @param {string} value
1812 * @param {!WebInspector.NetworkRequest} request
1815 WebInspector.NetworkLogView._requestSchemeFilter = function(value, request)
1817 return request.scheme === value;
1821 * @param {string} value
1822 * @param {!WebInspector.NetworkRequest} request
1825 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1827 var cookies = request.responseCookies;
1828 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1829 if (cookies[i].domain() === value)
1836 * @param {string} value
1837 * @param {!WebInspector.NetworkRequest} request
1840 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1842 var cookies = request.responseCookies;
1843 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1844 if (cookies[i].name() === value)
1851 * @param {string} value
1852 * @param {!WebInspector.NetworkRequest} request
1855 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1857 var cookies = request.responseCookies;
1858 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1859 if (cookies[i].value() === value)
1866 * @param {string} value
1867 * @param {!WebInspector.NetworkRequest} request
1870 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1872 return ("" + request.statusCode) === value;
1876 * @param {!WebInspector.NetworkRequest} request
1879 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1881 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1885 * @param {!WebInspector.NetworkRequest} request
1888 WebInspector.NetworkLogView.NonDevToolsRequestsFilter = function(request)
1890 return !WebInspector.NetworkManager.hasDevToolsRequestHeader(request);
1894 * @param {!WebInspector.NetworkRequest} request
1897 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1899 return request.finished;
1902 WebInspector.NetworkLogView.EventTypes = {
1903 ViewCleared: "ViewCleared",
1904 RowSizeChanged: "RowSizeChanged",
1905 RequestSelected: "RequestSelected",
1906 SearchCountUpdated: "SearchCountUpdated",
1907 SearchIndexUpdated: "SearchIndexUpdated"
1912 * @implements {WebInspector.ContextMenu.Provider}
1913 * @implements {WebInspector.Searchable}
1914 * @extends {WebInspector.Panel}
1916 WebInspector.NetworkPanel = function()
1918 WebInspector.Panel.call(this, "network");
1919 this.registerRequiredCSS("network/networkPanel.css");
1921 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1922 this._filterBar = new WebInspector.FilterBar();
1923 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1924 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1925 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1926 this._filterBar.setName("networkPanel");
1928 this._searchableView = new WebInspector.SearchableView(this);
1929 this._searchableView.show(this.element);
1930 var contentsElement = this._searchableView.element;
1932 this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1933 this._splitView.show(contentsElement);
1934 this._splitView.hideMain();
1936 var defaultColumnsVisibility = WebInspector.NetworkLogView.defaultColumnsVisibility;
1937 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1938 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1939 var columnsVisibility = {};
1940 for (var columnId in defaultColumnsVisibility)
1941 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1942 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1944 /** @type {!WebInspector.NetworkLogView} */
1945 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1946 this._networkLogView.show(this._splitView.sidebarElement());
1948 var viewsContainerView = new WebInspector.VBox();
1949 this._viewsContainerElement = viewsContainerView.element;
1950 this._viewsContainerElement.id = "network-views";
1951 if (!this._networkLogView.usesLargeRows())
1952 this._viewsContainerElement.classList.add("small");
1953 viewsContainerView.show(this._splitView.mainElement());
1955 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1956 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1957 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1958 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1959 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1961 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1962 this._closeButtonElement.id = "network-close-button";
1963 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1964 this._viewsContainerElement.appendChild(this._closeButtonElement);
1966 var statusBarItems = this._networkLogView.statusBarItems();
1967 for (var i = 0; i < statusBarItems.length; ++i)
1968 this._panelStatusBarElement.appendChild(statusBarItems[i]);
1971 * @this {WebInspector.NetworkPanel}
1972 * @return {?WebInspector.SourceFrame}
1974 function sourceFrameGetter()
1976 return this._networkItemView.currentSourceFrame();
1978 WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1981 WebInspector.NetworkPanel.prototype = {
1983 * @param {!WebInspector.Event} event
1985 _onFiltersToggled: function(event)
1987 var toggled = /** @type {boolean} */ (event.data);
1988 this._filtersContainer.classList.toggle("hidden", !toggled);
1989 this.element.classList.toggle("filters-toggled", toggled);
1994 * @return {!Array.<!Element>}
1996 elementsToRestoreScrollPositionsFor: function()
1998 return this._networkLogView.elementsToRestoreScrollPositionsFor();
2002 * @return {!WebInspector.SearchableView}
2004 searchableView: function()
2006 return this._searchableView;
2010 * @param {!KeyboardEvent} event
2012 handleShortcut: function(event)
2014 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
2015 this._toggleGridMode();
2016 event.handled = true;
2020 WebInspector.Panel.prototype.handleShortcut.call(this, event);
2023 wasShown: function()
2025 WebInspector.Panel.prototype.wasShown.call(this);
2029 * @param {!WebInspector.NetworkRequest} request
2031 revealAndHighlightRequest: function(request)
2033 this._toggleGridMode();
2035 this._networkLogView.revealAndHighlightRequest(request);
2039 * @param {!WebInspector.Event} event
2041 _onViewCleared: function(event)
2043 this._closeVisibleRequest();
2044 this._toggleGridMode();
2045 this._viewsContainerElement.removeChildren();
2046 this._viewsContainerElement.appendChild(this._closeButtonElement);
2050 * @param {!WebInspector.Event} event
2052 _onRowSizeChanged: function(event)
2054 this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
2058 * @param {!WebInspector.Event} event
2060 _onSearchCountUpdated: function(event)
2062 var count = /** @type {number} */ (event.data);
2063 this._searchableView.updateSearchMatchesCount(count);
2067 * @param {!WebInspector.Event} event
2069 _onSearchIndexUpdated: function(event)
2071 var index = /** @type {number} */ (event.data);
2072 this._searchableView.updateCurrentMatchIndex(index);
2076 * @param {!WebInspector.Event} event
2078 _onRequestSelected: function(event)
2080 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
2081 this._showRequest(request);
2085 * @param {?WebInspector.NetworkRequest} request
2087 _showRequest: function(request)
2092 this._toggleViewingRequestMode();
2094 if (this._networkItemView) {
2095 this._networkItemView.detach();
2096 delete this._networkItemView;
2099 var view = new WebInspector.NetworkItemView(request);
2100 view.show(this._viewsContainerElement);
2101 this._networkItemView = view;
2104 _closeVisibleRequest: function()
2106 this.element.classList.remove("viewing-resource");
2108 if (this._networkItemView) {
2109 this._networkItemView.detach();
2110 delete this._networkItemView;
2114 _toggleGridMode: function()
2116 if (this._viewingRequestMode) {
2117 this._viewingRequestMode = false;
2118 this.element.classList.remove("viewing-resource");
2119 this._splitView.hideMain();
2122 this._networkLogView.switchViewMode(true);
2123 this._networkLogView.setAllowPopover(true);
2124 this._networkLogView._allowRequestSelection = false;
2127 _toggleViewingRequestMode: function()
2129 if (this._viewingRequestMode)
2131 this._viewingRequestMode = true;
2133 this.element.classList.add("viewing-resource");
2134 this._splitView.showBoth();
2135 this._networkLogView.setAllowPopover(false);
2136 this._networkLogView._allowRequestSelection = true;
2137 this._networkLogView.switchViewMode(false);
2141 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
2142 * @param {boolean} shouldJump
2143 * @param {boolean=} jumpBackwards
2145 performSearch: function(searchConfig, shouldJump, jumpBackwards)
2147 this._networkLogView.performSearch(searchConfig, shouldJump, jumpBackwards);
2150 jumpToPreviousSearchResult: function()
2152 this._networkLogView.jumpToPreviousSearchResult();
2158 supportsCaseSensitiveSearch: function()
2166 supportsRegexSearch: function()
2171 jumpToNextSearchResult: function()
2173 this._networkLogView.jumpToNextSearchResult();
2176 searchCanceled: function()
2178 this._networkLogView.searchCanceled();
2182 * @param {!Event} event
2183 * @param {!WebInspector.ContextMenu} contextMenu
2184 * @param {!Object} target
2185 * @this {WebInspector.NetworkPanel}
2187 appendApplicableItems: function(event, contextMenu, target)
2190 * @this {WebInspector.NetworkPanel}
2192 function reveal(request)
2194 WebInspector.inspectorView.setCurrentPanel(this);
2195 this.revealAndHighlightRequest(request);
2199 * @this {WebInspector.NetworkPanel}
2201 function appendRevealItem(request)
2203 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
2204 contextMenu.appendItem(revealText, reveal.bind(this, request));
2207 if (target instanceof WebInspector.Resource) {
2208 var resource = /** @type {!WebInspector.Resource} */ (target);
2209 if (resource.request)
2210 appendRevealItem.call(this, resource.request);
2213 if (target instanceof WebInspector.UISourceCode) {
2214 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
2215 var resource = WebInspector.resourceForURL(uiSourceCode.url);
2216 if (resource && resource.request)
2217 appendRevealItem.call(this, resource.request);
2221 if (!(target instanceof WebInspector.NetworkRequest))
2223 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2224 if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2227 appendRevealItem.call(this, request);
2230 __proto__: WebInspector.Panel.prototype
2235 * @implements {WebInspector.ContextMenu.Provider}
2237 WebInspector.NetworkPanel.ContextMenuProvider = function()
2241 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2243 * @param {!Event} event
2244 * @param {!WebInspector.ContextMenu} contextMenu
2245 * @param {!Object} target
2247 appendApplicableItems: function(event, contextMenu, target)
2249 WebInspector.NetworkPanel._instance().appendApplicableItems(event, contextMenu, target);
2255 * @implements {WebInspector.Revealer}
2257 WebInspector.NetworkPanel.RequestRevealer = function()
2261 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2263 * @param {!Object} request
2264 * @param {number=} lineNumber
2265 * @return {!Promise}
2267 reveal: function(request, lineNumber)
2269 if (request instanceof WebInspector.NetworkRequest) {
2271 var panel = WebInspector.NetworkPanel._instance();
2272 WebInspector.inspectorView.setCurrentPanel(panel);
2273 panel.revealAndHighlightRequest(request);
2274 return Promise.resolve();
2276 return Promise.rejectWithError("Internal error: not a network request");
2282 * @implements {WebInspector.TimelineGrid.Calculator}
2284 WebInspector.NetworkTimeCalculator = function(startAtZero)
2286 this.startAtZero = startAtZero;
2289 /** @type {!WebInspector.UIStringFormat} */
2290 WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat = new WebInspector.UIStringFormat("%s latency, %s download (%s total)");
2292 /** @type {!WebInspector.UIStringFormat} */
2293 WebInspector.NetworkTimeCalculator._latencyFormat = new WebInspector.UIStringFormat("%s latency");
2295 /** @type {!WebInspector.UIStringFormat} */
2296 WebInspector.NetworkTimeCalculator._downloadFormat = new WebInspector.UIStringFormat("%s download");
2298 /** @type {!WebInspector.UIStringFormat} */
2299 WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat = new WebInspector.UIStringFormat("%s (from ServiceWorker)");
2301 /** @type {!WebInspector.UIStringFormat} */
2302 WebInspector.NetworkTimeCalculator._fromCacheFormat = new WebInspector.UIStringFormat("%s (from cache)");
2304 WebInspector.NetworkTimeCalculator.prototype = {
2309 paddingLeft: function()
2316 * @param {number} time
2319 computePosition: function(time)
2321 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2326 * @param {number} value
2327 * @param {number=} precision
2330 formatTime: function(value, precision)
2332 return Number.secondsToString(value);
2339 minimumBoundary: function()
2341 return this._minimumBoundary;
2348 zeroTime: function()
2350 return this._minimumBoundary;
2357 maximumBoundary: function()
2359 return this._maximumBoundary;
2366 boundarySpan: function()
2368 return this._maximumBoundary - this._minimumBoundary;
2373 delete this._minimumBoundary;
2374 delete this._maximumBoundary;
2380 _value: function(item)
2386 * @param {number} clientWidth
2388 setDisplayWindow: function(clientWidth)
2390 this._workingArea = clientWidth;
2394 * @param {!WebInspector.NetworkRequest} request
2395 * @return {!{start: number, middle: number, end: number}}
2397 computeBarGraphPercentages: function(request)
2399 if (request.startTime !== -1)
2400 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2404 if (request.responseReceivedTime !== -1)
2405 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2407 var middle = (this.startAtZero ? start : 100);
2409 if (request.endTime !== -1)
2410 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2412 var end = (this.startAtZero ? middle : 100);
2414 if (this.startAtZero) {
2420 return {start: start, middle: middle, end: end};
2424 * @param {number} eventTime
2427 computePercentageFromEventTime: function(eventTime)
2429 // This function computes a percentage in terms of the total loading time
2430 // of a specific event. If startAtZero is set, then this is useless, and we
2431 // want to return 0.
2432 if (eventTime !== -1 && !this.startAtZero)
2433 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2439 * @param {number} eventTime
2442 updateBoundariesForEventTime: function(eventTime)
2444 if (eventTime === -1 || this.startAtZero)
2447 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2448 this._maximumBoundary = eventTime;
2455 * @param {!WebInspector.NetworkRequest} request
2456 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2458 computeBarGraphLabels: function(request)
2460 var rightLabel = "";
2461 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2462 rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2464 var hasLatency = request.latency > 0;
2466 var leftLabel = Number.secondsToString(request.latency);
2468 var leftLabel = rightLabel;
2471 return {left: leftLabel, right: rightLabel};
2473 if (hasLatency && rightLabel) {
2474 var total = Number.secondsToString(request.duration);
2475 var tooltip = WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat.format(leftLabel, rightLabel, total);
2476 } else if (hasLatency)
2477 var tooltip = WebInspector.NetworkTimeCalculator._latencyFormat.format(leftLabel);
2478 else if (rightLabel)
2479 var tooltip = WebInspector.NetworkTimeCalculator._downloadFormat.format(rightLabel);
2481 if (request.fetchedViaServiceWorker)
2482 tooltip = WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat.format(tooltip);
2483 else if (request.cached())
2484 tooltip = WebInspector.NetworkTimeCalculator._fromCacheFormat.format(tooltip);
2485 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2489 * @param {!WebInspector.NetworkRequest} request
2492 updateBoundaries: function(request)
2494 var didChange = false;
2497 if (this.startAtZero)
2500 lowerBound = this._lowerBound(request);
2502 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2503 this._minimumBoundary = lowerBound;
2507 var upperBound = this._upperBound(request);
2508 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2509 this._maximumBoundary = upperBound;
2517 * @param {!WebInspector.NetworkRequest} request
2520 _lowerBound: function(request)
2526 * @param {!WebInspector.NetworkRequest} request
2529 _upperBound: function(request)
2537 * @extends {WebInspector.NetworkTimeCalculator}
2539 WebInspector.NetworkTransferTimeCalculator = function()
2541 WebInspector.NetworkTimeCalculator.call(this, false);
2544 WebInspector.NetworkTransferTimeCalculator.prototype = {
2547 * @param {number} value
2548 * @param {number=} precision
2551 formatTime: function(value, precision)
2553 return Number.secondsToString(value - this.zeroTime());
2558 * @param {!WebInspector.NetworkRequest} request
2561 _lowerBound: function(request)
2563 return request.startTime;
2568 * @param {!WebInspector.NetworkRequest} request
2571 _upperBound: function(request)
2573 return request.endTime;
2576 __proto__: WebInspector.NetworkTimeCalculator.prototype
2581 * @extends {WebInspector.NetworkTimeCalculator}
2583 WebInspector.NetworkTransferDurationCalculator = function()
2585 WebInspector.NetworkTimeCalculator.call(this, true);
2588 WebInspector.NetworkTransferDurationCalculator.prototype = {
2591 * @param {number} value
2592 * @param {number=} precision
2595 formatTime: function(value, precision)
2597 return Number.secondsToString(value);
2602 * @param {!WebInspector.NetworkRequest} request
2605 _upperBound: function(request)
2607 return request.duration;
2610 __proto__: WebInspector.NetworkTimeCalculator.prototype
2615 * @extends {WebInspector.SortableDataGridNode}
2616 * @param {!WebInspector.NetworkLogView} parentView
2617 * @param {!WebInspector.NetworkRequest} request
2619 WebInspector.NetworkDataGridNode = function(parentView, request)
2621 WebInspector.SortableDataGridNode.call(this, {});
2622 this._parentView = parentView;
2623 this._request = request;
2624 this._linkifier = new WebInspector.Linkifier();
2625 this._isFilteredOut = true;
2626 this._isMatchingSearchQuery = false;
2627 this._staleGraph = true;
2630 WebInspector.NetworkDataGridNode._hoveredRowSymbol = Symbol("hoveredRow");
2632 WebInspector.NetworkDataGridNode.prototype = {
2634 * @return {!WebInspector.NetworkRequest}
2638 return this._request;
2645 nodeSelfHeight: function()
2647 return this._parentView.rowHeight();
2651 createCells: function()
2653 this._nameCell = null;
2654 this._timelineCell = null;
2655 this._initiatorCell = null;
2657 this._element.classList.toggle("network-error-row", this._isFailed());
2658 WebInspector.SortableDataGridNode.prototype.createCells.call(this);
2660 this._updateGraph();
2665 * @param {string} columnIdentifier
2666 * @return {!Element}
2668 createCell: function(columnIdentifier)
2670 var cell = this.createTD(columnIdentifier);
2671 switch (columnIdentifier) {
2672 case "name": this._renderNameCell(cell); break;
2673 case "timeline": this._createTimelineBar(cell); break;
2674 case "method": cell.setTextAndTitle(this._request.requestMethod); break;
2675 case "status": this._renderStatusCell(cell); break;
2676 case "scheme": cell.setTextAndTitle(this._request.scheme); break;
2677 case "domain": cell.setTextAndTitle(this._request.domain); break;
2678 case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
2679 case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
2680 case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
2681 case "connectionId": cell.setTextAndTitle(this._request.connectionId); break;
2682 case "type": cell.setTextAndTitle(this._request.mimeType || this._request.requestContentType() || ""); break;
2683 case "initiator": this._renderInitiatorCell(cell); break;
2684 case "size": this._renderSizeCell(cell); break;
2685 case "time": this._renderTimeCell(cell); break;
2686 default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
2693 * @param {?Array} array
2696 _arrayLength: function(array)
2698 return array ? "" + array.length : "";
2705 willAttach: function()
2707 if (this._staleGraph)
2708 this._updateGraph();
2709 if (this._initiatorCell && this._request.initiatorInfo().type === WebInspector.NetworkRequest.InitiatorType.Script)
2710 this._initiatorCell.insertBefore(this._linkifiedInitiatorAnchor, this._initiatorCell.firstChild);
2713 wasDetached: function()
2715 if (this._linkifiedInitiatorAnchor)
2716 this._linkifiedInitiatorAnchor.remove();
2721 this._linkifier.reset();
2724 _onClick: function()
2726 if (!this._parentView.allowRequestSelection())
2732 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2733 WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
2735 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2736 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2737 url: this._request.url
2742 * @param {!RegExp=} regexp
2743 * @return {!Array.<!Object>}
2745 highlightMatchedSubstring: function(regexp)
2747 // Ensure element is created.
2749 var domChanges = [];
2750 var matchInfo = this._nameCell.textContent.match(regexp);
2752 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2756 _openInNewTab: function()
2758 InspectorFrontendHost.openInNewTab(this._request.url);
2763 return this._parentView.allowRequestSelection();
2767 * @param {!Element} cell
2769 _createTimelineBar: function(cell)
2771 cell = cell.createChild("div");
2772 this._timelineCell = cell;
2774 cell.className = "network-graph-side";
2776 this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
2777 this._barAreaElement.request = this._request;
2779 var type = this._request.resourceType().name();
2780 var cached = this._request.cached();
2782 this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
2783 this._barLeftElement.classList.add(type, "waiting");
2784 this._barLeftElement.classList.toggle("cached", cached);
2786 this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
2787 this._barRightElement.classList.add(type);
2788 this._barRightElement.classList.toggle("cached", cached);
2790 this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
2791 this._labelLeftElement.classList.add("waiting");
2793 this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
2795 cell.addEventListener("mouseover", this._onMouseOver.bind(this), false);
2799 * @param {!Event} event
2801 _onMouseOver: function(event)
2803 this._refreshLabelPositions();
2804 this._parentView[WebInspector.NetworkDataGridNode._hoveredRowSymbol] = this;
2810 _isFailed: function()
2812 return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
2816 * @param {!Element} cell
2818 _renderNameCell: function(cell)
2820 this._nameCell = cell;
2821 cell.addEventListener("click", this._onClick.bind(this), false);
2822 cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2824 if (this._request.resourceType() === WebInspector.resourceTypes.Image) {
2825 var previewImage = createElementWithClass("img", "image-network-icon-preview");
2826 this._request.populateImageSource(previewImage);
2828 iconElement = createElementWithClass("div", "icon");
2829 iconElement.appendChild(previewImage);
2831 iconElement = createElementWithClass("img", "icon");
2833 iconElement.classList.add(this._request.resourceType().name());
2835 cell.appendChild(iconElement);
2836 cell.createTextChild(this._request.name());
2837 this._appendSubtitle(cell, this._request.path());
2838 cell.title = this._request.url;
2842 * @param {!Element} cell
2844 _renderStatusCell: function(cell)
2846 cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached() || !this._request.statusCode));
2848 if (this._request.failed && !this._request.canceled) {
2849 var failText = WebInspector.UIString("(failed)");
2850 if (this._request.localizedFailDescription) {
2851 cell.createTextChild(failText);
2852 this._appendSubtitle(cell, this._request.localizedFailDescription);
2853 cell.title = failText + " " + this._request.localizedFailDescription;
2855 cell.setTextAndTitle(failText);
2856 } else if (this._request.statusCode) {
2857 cell.createTextChild("" + this._request.statusCode);
2858 this._appendSubtitle(cell, this._request.statusText);
2859 cell.title = this._request.statusCode + " " + this._request.statusText;
2860 } else if (this._request.parsedURL.isDataURL()) {
2861 cell.setTextAndTitle(WebInspector.UIString("(data)"));
2862 } else if (this._request.canceled) {
2863 cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2864 } else if (this._request.finished) {
2865 cell.setTextAndTitle(WebInspector.UIString("Finished"));
2867 cell.setTextAndTitle(WebInspector.UIString("(pending)"));
2872 * @param {!Element} cell
2874 _renderInitiatorCell: function(cell)
2876 this._initiatorCell = cell;
2877 var request = this._request;
2878 var initiator = request.initiatorInfo();
2880 switch (initiator.type) {
2881 case WebInspector.NetworkRequest.InitiatorType.Parser:
2882 cell.title = initiator.url + ":" + initiator.lineNumber;
2883 cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2884 this._appendSubtitle(cell, WebInspector.UIString("Parser"));
2887 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2888 cell.title = initiator.url;
2889 console.assert(request.redirectSource);
2890 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2891 cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2892 this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
2895 case WebInspector.NetworkRequest.InitiatorType.Script:
2896 if (!this._linkifiedInitiatorAnchor) {
2897 this._linkifiedInitiatorAnchor = this._linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2898 this._linkifiedInitiatorAnchor.title = "";
2900 cell.appendChild(this._linkifiedInitiatorAnchor);
2901 this._appendSubtitle(cell, WebInspector.UIString("Script"));
2902 cell.classList.add("network-script-initiated");
2903 cell.request = request;
2908 cell.classList.add("network-dim-cell");
2909 cell.setTextAndTitle(WebInspector.UIString("Other"));
2914 * @param {!Element} cell
2916 _renderSizeCell: function(cell)
2918 if (this._request.fetchedViaServiceWorker) {
2919 cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)"));
2920 cell.classList.add("network-dim-cell");
2921 } else if (this._request.cached()) {
2922 cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2923 cell.classList.add("network-dim-cell");
2925 var resourceSize = Number.bytesToString(this._request.resourceSize);
2926 var transferSize = Number.bytesToString(this._request.transferSize);
2927 cell.setTextAndTitle(transferSize);
2928 this._appendSubtitle(cell, resourceSize);
2933 * @param {!Element} cell
2935 _renderTimeCell: function(cell)
2937 if (this._request.duration > 0) {
2938 cell.setTextAndTitle(Number.secondsToString(this._request.duration));
2939 this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
2941 cell.classList.add("network-dim-cell");
2942 cell.setTextAndTitle(WebInspector.UIString("Pending"));
2947 * @param {!Element} cellElement
2948 * @param {string} subtitleText
2950 _appendSubtitle: function(cellElement, subtitleText)
2952 var subtitleElement = createElement("div");
2953 subtitleElement.className = "network-cell-subtitle";
2954 subtitleElement.textContent = subtitleText;
2955 cellElement.appendChild(subtitleElement);
2958 refreshGraph: function()
2960 if (!this._timelineCell)
2962 this._staleGraph = true;
2963 if (this.attached())
2964 this.dataGrid.scheduleUpdate();
2967 _updateGraph: function()
2969 this._staleGraph = false;
2970 if (!this._timelineCell)
2973 var calculator = this._parentView.calculator();
2974 var percentages = calculator.computeBarGraphPercentages(this._request);
2975 this._percentages = percentages;
2977 this._barAreaElement.classList.remove("hidden");
2979 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2980 this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
2982 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2983 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2985 var labels = calculator.computeBarGraphLabels(this._request);
2986 this._labelLeftElement.textContent = labels.left;
2987 this._labelRightElement.textContent = labels.right;
2989 var tooltip = (labels.tooltip || "");
2990 this._barLeftElement.title = tooltip;
2991 this._labelLeftElement.title = tooltip;
2992 this._labelRightElement.title = tooltip;
2993 this._barRightElement.title = tooltip;
2995 if (this._parentView[WebInspector.NetworkDataGridNode._hoveredRowSymbol] === this)
2996 this._refreshLabelPositions();
2999 _refreshLabelPositions: function()
3001 if (!this._percentages)
3003 this._labelLeftElement.style.removeProperty("left");
3004 this._labelLeftElement.style.removeProperty("right");
3005 this._labelLeftElement.classList.remove("before");
3006 this._labelLeftElement.classList.remove("hidden");
3008 this._labelRightElement.style.removeProperty("left");
3009 this._labelRightElement.style.removeProperty("right");
3010 this._labelRightElement.classList.remove("after");
3011 this._labelRightElement.classList.remove("hidden");
3013 const labelPadding = 10;
3014 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
3015 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
3017 if (this._barLeftElement) {
3018 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
3019 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
3021 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
3022 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
3025 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
3026 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
3028 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
3029 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
3030 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
3032 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
3033 var leftHidden = true;
3035 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
3036 var rightHidden = true;
3038 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
3039 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
3040 if (labelBefore && !labelAfter)
3042 else if (labelAfter && !labelBefore)
3048 this._labelLeftElement.classList.add("hidden");
3049 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
3050 this._labelLeftElement.classList.add("before");
3052 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
3053 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
3058 this._labelRightElement.classList.add("hidden");
3059 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
3060 this._labelRightElement.classList.add("after");
3062 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
3063 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
3067 __proto__: WebInspector.SortableDataGridNode.prototype
3071 * @param {!WebInspector.NetworkDataGridNode} a
3072 * @param {!WebInspector.NetworkDataGridNode} b
3075 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
3077 var aFileName = a._request.name();
3078 var bFileName = b._request.name();
3079 if (aFileName > bFileName)
3081 if (bFileName > aFileName)
3083 return a._request.indentityCompare(b._request);
3087 * @param {!WebInspector.NetworkDataGridNode} a
3088 * @param {!WebInspector.NetworkDataGridNode} b
3091 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
3093 var aRemoteAddress = a._request.remoteAddress();
3094 var bRemoteAddress = b._request.remoteAddress();
3095 if (aRemoteAddress > bRemoteAddress)
3097 if (bRemoteAddress > aRemoteAddress)
3099 return a._request.indentityCompare(b._request);
3103 * @param {!WebInspector.NetworkDataGridNode} a
3104 * @param {!WebInspector.NetworkDataGridNode} b
3107 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
3109 if (b._request.cached() && !a._request.cached())
3111 if (a._request.cached() && !b._request.cached())
3113 return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
3117 * @param {!WebInspector.NetworkDataGridNode} a
3118 * @param {!WebInspector.NetworkDataGridNode} b
3121 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
3123 var aInitiator = a._request.initiatorInfo();
3124 var bInitiator = b._request.initiatorInfo();
3126 if (aInitiator.type < bInitiator.type)
3128 if (aInitiator.type > bInitiator.type)
3131 if (typeof aInitiator.__source === "undefined")
3132 aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
3133 if (typeof bInitiator.__source === "undefined")
3134 bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
3136 if (aInitiator.__source < bInitiator.__source)
3138 if (aInitiator.__source > bInitiator.__source)
3141 if (aInitiator.lineNumber < bInitiator.lineNumber)
3143 if (aInitiator.lineNumber > bInitiator.lineNumber)
3146 if (aInitiator.columnNumber < bInitiator.columnNumber)
3148 if (aInitiator.columnNumber > bInitiator.columnNumber)
3151 return a._request.indentityCompare(b._request);
3155 * @param {!WebInspector.NetworkDataGridNode} a
3156 * @param {!WebInspector.NetworkDataGridNode} b
3159 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
3161 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
3162 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
3163 return (aScore - bScore) || a._request.indentityCompare(b._request);
3167 * @param {!WebInspector.NetworkDataGridNode} a
3168 * @param {!WebInspector.NetworkDataGridNode} b
3171 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
3173 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
3174 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
3175 return (aScore - bScore) || a._request.indentityCompare(b._request);
3179 * @param {string} propertyName
3180 * @param {boolean} revert
3181 * @param {!WebInspector.NetworkDataGridNode} a
3182 * @param {!WebInspector.NetworkDataGridNode} b
3185 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
3187 var aValue = a._request[propertyName];
3188 var bValue = b._request[propertyName];
3189 if (aValue > bValue)
3190 return revert ? -1 : 1;
3191 if (bValue > aValue)
3192 return revert ? 1 : -1;
3193 return a._request.indentityCompare(b._request);
3196 WebInspector.NetworkPanel.show = function()
3198 WebInspector.inspectorView.setCurrentPanel(WebInspector.NetworkPanel._instance());
3202 * @return {!WebInspector.NetworkPanel}
3204 WebInspector.NetworkPanel._instance = function()
3206 if (!WebInspector.NetworkPanel._instanceObject)
3207 WebInspector.NetworkPanel._instanceObject = new WebInspector.NetworkPanel();
3208 return WebInspector.NetworkPanel._instanceObject;
3213 * @implements {WebInspector.PanelFactory}
3215 WebInspector.NetworkPanelFactory = function()
3219 WebInspector.NetworkPanelFactory.prototype = {
3221 * @return {!WebInspector.Panel}
3223 createPanel: function()
3225 return WebInspector.NetworkPanel._instance();
3232 WebInspector.HARWriter = function()
3236 WebInspector.HARWriter.prototype = {
3238 * @param {!WebInspector.OutputStream} stream
3239 * @param {!Array.<!WebInspector.NetworkRequest>} requests
3240 * @param {!WebInspector.Progress} progress
3242 write: function(stream, requests, progress)
3244 this._stream = stream;
3245 this._harLog = (new WebInspector.HARLog(requests)).build();
3246 this._pendingRequests = 1; // Guard against completing resource transfer before all requests are made.
3247 var entries = this._harLog.entries;
3248 for (var i = 0; i < entries.length; ++i) {
3249 var content = requests[i].content;
3250 if (typeof content === "undefined" && requests[i].finished) {
3251 ++this._pendingRequests;
3252 requests[i].requestContent(this._onContentAvailable.bind(this, entries[i], requests[i]));
3253 } else if (content !== null)
3254 this._setEntryContent(entries[i], requests[i]);
3256 var compositeProgress = new WebInspector.CompositeProgress(progress);
3257 this._writeProgress = compositeProgress.createSubProgress();
3258 if (--this._pendingRequests) {
3259 this._requestsProgress = compositeProgress.createSubProgress();
3260 this._requestsProgress.setTitle(WebInspector.UIString("Collecting content…"));
3261 this._requestsProgress.setTotalWork(this._pendingRequests);
3267 * @param {!Object} entry
3268 * @param {!WebInspector.NetworkRequest} request
3270 _setEntryContent: function(entry, request)
3272 if (request.content !== null)
3273 entry.response.content.text = request.content;
3274 if (request.contentEncoded)
3275 entry.response.content.encoding = "base64";
3279 * @param {!Object} entry
3280 * @param {!WebInspector.NetworkRequest} request
3281 * @param {?string} content
3283 _onContentAvailable: function(entry, request, content)
3285 this._setEntryContent(entry, request);
3286 if (this._requestsProgress)
3287 this._requestsProgress.worked();
3288 if (!--this._pendingRequests) {
3289 this._requestsProgress.done();
3294 _beginWrite: function()
3296 const jsonIndent = 2;
3297 this._text = JSON.stringify({log: this._harLog}, null, jsonIndent);
3298 this._writeProgress.setTitle(WebInspector.UIString("Writing file…"));
3299 this._writeProgress.setTotalWork(this._text.length);
3300 this._bytesWritten = 0;
3301 this._writeNextChunk(this._stream);
3305 * @param {!WebInspector.OutputStream} stream
3306 * @param {string=} error
3308 _writeNextChunk: function(stream, error)
3310 if (this._bytesWritten >= this._text.length || error) {
3312 this._writeProgress.done();
3315 const chunkSize = 100000;
3316 var text = this._text.substring(this._bytesWritten, this._bytesWritten + chunkSize);
3317 this._bytesWritten += text.length;
3318 stream.write(text, this._writeNextChunk.bind(this));
3319 this._writeProgress.setWorked(this._bytesWritten);