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.
31 importScript("RequestView.js");
32 importScript("NetworkItemView.js");
33 importScript("RequestCookiesView.js");
34 importScript("RequestHeadersView.js");
35 importScript("RequestHTMLView.js");
36 importScript("RequestJSONView.js");
37 importScript("RequestPreviewView.js");
38 importScript("RequestResponseView.js");
39 importScript("RequestTimingView.js");
40 importScript("ResourceWebSocketFrameView.js");
44 * @implements {WebInspector.Searchable}
45 * @implements {WebInspector.TargetManager.Observer}
46 * @extends {WebInspector.VBox}
47 * @param {!WebInspector.FilterBar} filterBar
48 * @param {!WebInspector.Setting} coulmnsVisibilitySetting
50 WebInspector.NetworkLogView = function(filterBar, coulmnsVisibilitySetting)
52 WebInspector.VBox.call(this);
53 this.registerRequiredCSS("networkLogView.css");
54 this.registerRequiredCSS("filter.css");
55 this.registerRequiredCSS("suggestBox.css");
57 this._filterBar = filterBar;
58 this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
59 this._allowRequestSelection = false;
61 this._requestsById = {};
62 this._requestsByURL = {};
63 this._staleRequests = {};
64 this._requestGridNodes = {};
65 this._lastRequestGridNodeId = 0;
66 this._mainRequestLoadTime = -1;
67 this._mainRequestDOMContentLoadedTime = -1;
68 this._matchedRequests = [];
69 this._highlightedSubstringChanges = [];
70 this._filteredOutRequests = new Map();
72 /** @type {!Array.<!WebInspector.NetworkLogView.Filter>} */
75 this._matchedRequestsMap = {};
76 this._currentMatchedRequestIndex = -1;
78 this._createStatusbarButtons();
79 this._createStatusBarItems();
80 this._linkifier = new WebInspector.Linkifier();
83 this._resetSuggestionBuilder();
84 this._initializeView();
85 this._recordButton.toggled = true;
87 WebInspector.targetManager.observeTargets(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,
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;
98 WebInspector.NetworkLogView.prototype = {
100 * @param {!WebInspector.Target} target
102 targetAdded: function(target)
104 target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
105 target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
106 target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
108 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
109 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
110 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
111 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
113 target.networkLog.requests.forEach(this._appendRequest.bind(this));
117 * @param {!WebInspector.Target} target
119 targetRemoved: function(target)
121 target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
122 target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
123 target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
125 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
126 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
127 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
128 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
131 _addFilters: function()
133 this._textFilterUI = new WebInspector.TextFilterUI();
134 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
135 this._filterBar.addFilter(this._textFilterUI);
138 for (var typeId in WebInspector.resourceTypes) {
139 var resourceType = WebInspector.resourceTypes[typeId];
140 types.push({name: resourceType.name(), label: resourceType.categoryTitle()});
142 this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI(types, WebInspector.settings.networkResourceTypeFilters);
143 this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
144 this._filterBar.addFilter(this._resourceTypeFilterUI);
146 var dataURLSetting = WebInspector.settings.networkHideDataURL;
147 this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
148 this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
149 this._filterBar.addFilter(this._dataURLFilterUI);
152 _resetSuggestionBuilder: function()
154 this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkPanel._searchKeys);
155 this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
158 _filterChanged: function(event)
160 this._removeAllNodeHighlights();
161 this.searchCanceled();
162 this._parseFilterQuery(this._textFilterUI.value());
163 this._filterRequests();
166 _initializeView: function()
168 this.element.id = "network-container";
170 this._createSortingFunctions();
172 this._createTimelineGrid();
173 this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
175 if (!this.useLargeRows)
176 this._setLargerRequests(this.useLargeRows);
178 this._allowPopover = true;
179 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
180 // Enable faster hint.
181 this._popoverHelper.setTimeout(100);
183 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
185 this.switchToDetailedView();
191 this._recordButton.element,
192 this._clearButton.element,
193 this._filterBar.filterButton().element,
194 this._largerRequestsButton.element,
195 this._preserveLogCheckbox.element,
196 this._disableCacheCheckbox.element,
197 this._progressBarContainer];
202 return WebInspector.settings.resourcesLargeRows.get();
205 set allowPopover(flag)
207 this._allowPopover = flag;
211 * @return {!Array.<!Element>}
213 elementsToRestoreScrollPositionsFor: function()
215 if (!this._dataGrid) // Not initialized yet.
217 return [this._dataGrid.scrollContainer];
220 _createTimelineGrid: function()
222 this._timelineGrid = new WebInspector.TimelineGrid();
223 this._timelineGrid.element.classList.add("network-timeline-grid");
224 this._dataGrid.element.appendChild(this._timelineGrid.element);
227 _createTable: function()
232 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
233 title: WebInspector.UIString("Name"),
241 title: WebInspector.UIString("Method"),
248 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
249 title: WebInspector.UIString("Status"),
256 title: WebInspector.UIString("Scheme"),
263 title: WebInspector.UIString("Domain"),
270 title: WebInspector.UIString("Remote Address"),
273 align: WebInspector.DataGrid.Align.Right
278 title: WebInspector.UIString("Type"),
285 title: WebInspector.UIString("Initiator"),
292 title: WebInspector.UIString("Cookies"),
295 align: WebInspector.DataGrid.Align.Right
300 title: WebInspector.UIString("Set-Cookies"),
303 align: WebInspector.DataGrid.Align.Right
308 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
309 title: WebInspector.UIString("Size"),
312 align: WebInspector.DataGrid.Align.Right
317 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
318 title: WebInspector.UIString("Time"),
321 align: WebInspector.DataGrid.Align.Right
324 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
325 for (var i = 0; i < responseHeaderColumns.length; ++i) {
326 var headerName = responseHeaderColumns[i];
329 title: WebInspector.UIString(headerName),
332 if (headerName === "Content-Length")
333 descriptor.align = WebInspector.DataGrid.Align.Right;
334 columns.push(descriptor);
339 titleDOMFragment: document.createDocumentFragment(),
340 title: WebInspector.UIString("Timeline"),
343 sort: WebInspector.DataGrid.Order.Ascending
346 this._dataGrid = new WebInspector.DataGrid(columns);
347 this._dataGrid.setName("networkLog");
348 this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
349 this._dataGrid.element.classList.add("network-log-grid");
350 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
351 this._dataGrid.show(this.element);
353 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
354 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
355 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
357 this._patchTimelineHeader();
360 _makeHeaderFragment: function(title, subtitle)
362 var fragment = document.createDocumentFragment();
363 fragment.createTextChild(title);
364 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
365 subtitleDiv.createTextChild(subtitle);
369 _patchTimelineHeader: function()
371 var timelineSorting = document.createElement("select");
373 var option = document.createElement("option");
374 option.value = "startTime";
375 option.label = WebInspector.UIString("Timeline");
376 timelineSorting.appendChild(option);
378 option = document.createElement("option");
379 option.value = "startTime";
380 option.label = WebInspector.UIString("Start Time");
381 timelineSorting.appendChild(option);
383 option = document.createElement("option");
384 option.value = "responseTime";
385 option.label = WebInspector.UIString("Response Time");
386 timelineSorting.appendChild(option);
388 option = document.createElement("option");
389 option.value = "endTime";
390 option.label = WebInspector.UIString("End Time");
391 timelineSorting.appendChild(option);
393 option = document.createElement("option");
394 option.value = "duration";
395 option.label = WebInspector.UIString("Duration");
396 timelineSorting.appendChild(option);
398 option = document.createElement("option");
399 option.value = "latency";
400 option.label = WebInspector.UIString("Latency");
401 timelineSorting.appendChild(option);
403 var header = this._dataGrid.headerTableHeader("timeline");
404 header.replaceChild(timelineSorting, header.firstChild);
406 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
407 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
408 this._timelineSortSelector = timelineSorting;
411 _createSortingFunctions: function()
413 this._sortingFunctions = {};
414 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
415 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
416 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
417 this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
418 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
419 this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
420 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
421 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
422 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
423 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
424 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
425 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
426 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
427 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
428 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
429 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
430 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
431 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
433 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
434 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
436 this._calculators = {};
437 this._calculators.timeline = timeCalculator;
438 this._calculators.startTime = timeCalculator;
439 this._calculators.endTime = timeCalculator;
440 this._calculators.responseTime = timeCalculator;
441 this._calculators.duration = durationCalculator;
442 this._calculators.latency = durationCalculator;
445 _sortItems: function()
447 this._removeAllNodeHighlights();
448 var columnIdentifier = this._dataGrid.sortColumnIdentifier();
449 if (columnIdentifier === "timeline") {
450 this._sortByTimeline();
453 var sortingFunction = this._sortingFunctions[columnIdentifier];
454 if (!sortingFunction)
457 this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
458 this._timelineSortSelector.selectedIndex = 0;
460 this.searchCanceled();
462 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
463 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
464 column: columnIdentifier,
465 sortOrder: this._dataGrid.sortOrder()
469 _sortByTimeline: function()
471 this._removeAllNodeHighlights();
472 var selectedIndex = this._timelineSortSelector.selectedIndex;
474 selectedIndex = 1; // Sort by start time by default.
475 var selectedOption = this._timelineSortSelector[selectedIndex];
476 var value = selectedOption.value;
478 var sortingFunction = this._sortingFunctions[value];
479 this._dataGrid.sortNodes(sortingFunction);
480 this.calculator = this._calculators[value];
481 if (this.calculator.startAtZero)
482 this._timelineGrid.hideEventDividers();
484 this._timelineGrid.showEventDividers();
485 this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
488 _createStatusBarItems: function()
490 this._progressBarContainer = document.createElement("div");
491 this._progressBarContainer.className = "status-bar-item";
494 _updateSummaryBar: function()
496 var requestsNumber = this._requests.length;
498 if (!requestsNumber) {
499 if (this._summaryBarElement._isDisplayingWarning)
501 this._summaryBarElement._isDisplayingWarning = true;
502 this._summaryBarElement.removeChildren();
503 this._summaryBarElement.createChild("div", "warning-icon-small");
504 var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
505 this._summaryBarElement.appendChild(document.createTextNode(text));
506 this._summaryBarElement.title = text;
509 delete this._summaryBarElement._isDisplayingWarning;
511 var transferSize = 0;
512 var selectedRequestsNumber = 0;
513 var selectedTransferSize = 0;
516 for (var i = 0; i < this._requests.length; ++i) {
517 var request = this._requests[i];
518 var requestTransferSize = request.transferSize;
519 transferSize += requestTransferSize;
520 if (!this._filteredOutRequests.get(request)) {
521 selectedRequestsNumber++;
522 selectedTransferSize += requestTransferSize;
524 if (request.url === request.target().resourceTreeModel.inspectedPageURL())
525 baseTime = request.startTime;
526 if (request.endTime > maxTime)
527 maxTime = request.endTime;
530 if (selectedRequestsNumber !== requestsNumber) {
531 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
532 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
534 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
535 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
537 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
538 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
539 Number.secondsToString(maxTime - baseTime),
540 Number.secondsToString(this._mainRequestLoadTime - baseTime),
541 Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
543 this._summaryBarElement.textContent = text;
544 this._summaryBarElement.title = text;
547 _scheduleRefresh: function()
549 if (this._needsRefresh)
552 this._needsRefresh = true;
554 if (this.isShowing() && !this._refreshTimeout)
555 this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
558 _updateDividersIfNeeded: function()
562 var timelineColumn = this._dataGrid.columns.timeline;
563 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
564 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnIndex) {
565 // Position timline grid location.
566 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
571 if (!this.isShowing()) {
572 this._scheduleRefresh();
575 this.calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
576 proceed = this._timelineGrid.updateDividers(this.calculator);
581 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
582 // If our current sorting method starts at zero, that means it shows all
583 // requests starting at the same point, and so onLoad event and DOMContent
584 // event lines really wouldn't make much sense here, so don't render them.
585 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
586 // function defined, we are probably sorting by size, and event times aren't relevant
591 this._timelineGrid.removeEventDividers();
592 if (this._mainRequestLoadTime !== -1) {
593 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
595 var loadDivider = document.createElement("div");
596 loadDivider.className = "network-event-divider network-red-divider";
598 var loadDividerPadding = document.createElement("div");
599 loadDividerPadding.className = "network-event-divider-padding";
600 loadDividerPadding.title = WebInspector.UIString("Load event");
601 loadDividerPadding.appendChild(loadDivider);
602 loadDividerPadding.style.left = percent + "%";
603 this._timelineGrid.addEventDivider(loadDividerPadding);
606 if (this._mainRequestDOMContentLoadedTime !== -1) {
607 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
609 var domContentLoadedDivider = document.createElement("div");
610 domContentLoadedDivider.className = "network-event-divider network-blue-divider";
612 var domContentLoadedDividerPadding = document.createElement("div");
613 domContentLoadedDividerPadding.className = "network-event-divider-padding";
614 domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event");
615 domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
616 domContentLoadedDividerPadding.style.left = percent + "%";
617 this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
621 _refreshIfNeeded: function()
623 if (this._needsRefresh)
627 _invalidateAllItems: function()
629 for (var i = 0; i < this._requests.length; ++i) {
630 var request = this._requests[i];
631 this._staleRequests[request.requestId] = request;
637 return this._calculator;
642 if (!x || this._calculator === x)
645 this._calculator = x;
646 this._calculator.reset();
648 this._invalidateAllItems();
652 _requestGridNode: function(request)
654 return this._requestGridNodes[request.__gridNodeId];
657 _createRequestGridNode: function(request)
659 var node = new WebInspector.NetworkDataGridNode(this, request);
660 request.__gridNodeId = this._lastRequestGridNodeId++;
661 this._requestGridNodes[request.__gridNodeId] = node;
665 _createStatusbarButtons: function()
667 this._recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record Network Log"), "record-profile-status-bar-item");
668 this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
670 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
671 this._clearButton.addEventListener("click", this._reset, this);
673 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
674 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
675 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
677 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
678 this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
680 this._disableCacheCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Disable cache"));
681 WebInspector.SettingsUI.bindCheckbox(this._disableCacheCheckbox.inputElement, WebInspector.settings.cacheDisabled);
682 this._disableCacheCheckbox.element.title = WebInspector.UIString("Disable cache (while DevTools is open).");
685 _loadEventFired: function(event)
687 if (!this._recordButton.toggled)
690 this._mainRequestLoadTime = event.data || -1;
691 // Schedule refresh to update boundaries and draw the new line.
692 this._scheduleRefresh();
695 _domContentLoadedEventFired: function(event)
697 if (!this._recordButton.toggled)
699 this._mainRequestDOMContentLoadedTime = event.data || -1;
700 // Schedule refresh to update boundaries and draw the new line.
701 this._scheduleRefresh();
706 this._refreshIfNeeded();
711 this._popoverHelper.hidePopover();
716 this._needsRefresh = false;
717 if (this._refreshTimeout) {
718 clearTimeout(this._refreshTimeout);
719 delete this._refreshTimeout;
722 this._removeAllNodeHighlights();
723 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
724 var boundariesChanged = false;
725 if (this.calculator.updateBoundariesForEventTime) {
726 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
727 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
730 for (var requestId in this._staleRequests) {
731 var request = this._staleRequests[requestId];
732 var node = this._requestGridNode(request);
734 // Create the timeline tree element and graph.
735 node = this._createRequestGridNode(request);
736 this._dataGrid.rootNode().appendChild(node);
738 node.refreshRequest();
739 this._applyFilter(node);
741 if (this.calculator.updateBoundaries(request))
742 boundariesChanged = true;
744 if (!node.isFilteredOut())
745 this._updateHighlightIfMatched(request);
748 if (boundariesChanged) {
749 // The boundaries changed, so all item graphs are stale.
750 this._invalidateAllItems();
753 for (var requestId in this._staleRequests)
754 this._requestGridNode(this._staleRequests[requestId]).refreshGraph(this.calculator);
756 this._staleRequests = {};
758 this._updateSummaryBar();
759 this._dataGrid.updateWidths();
760 // FIXME: evaluate performance impact of moving this before a call to sortItems()
761 if (wasScrolledToLastRow)
762 this._dataGrid.scrollToLastRow();
765 _onRecordButtonClicked: function()
767 if (!this._recordButton.toggled)
769 this._recordButton.toggled = !this._recordButton.toggled;
774 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
776 this._clearSearchMatchedList();
777 if (this._popoverHelper)
778 this._popoverHelper.hidePopover();
780 if (this._calculator)
781 this._calculator.reset();
784 this._requestsById = {};
785 this._requestsByURL = {};
786 this._staleRequests = {};
787 this._requestGridNodes = {};
788 this._resetSuggestionBuilder();
790 if (this._dataGrid) {
791 this._dataGrid.rootNode().removeChildren();
792 this._updateDividersIfNeeded();
793 this._updateSummaryBar();
796 this._mainRequestLoadTime = -1;
797 this._mainRequestDOMContentLoadedTime = -1;
802 return this._requests;
805 _onRequestStarted: function(event)
807 if (this._recordButton.toggled)
808 this._appendRequest(event.data);
811 _appendRequest: function(request)
813 this._requests.push(request);
815 // In case of redirect request id is reassigned to a redirected
816 // request and we need to update _requestsById and search results.
817 if (this._requestsById[request.requestId]) {
818 var oldRequest = request.redirects[request.redirects.length - 1];
819 this._requestsById[oldRequest.requestId] = oldRequest;
821 this._updateSearchMatchedListAfterRequestIdChanged(request.requestId, oldRequest.requestId);
823 this._requestsById[request.requestId] = request;
825 this._requestsByURL[request.url] = request;
827 // Pull all the redirects of the main request upon commit load.
828 if (request.redirects) {
829 for (var i = 0; i < request.redirects.length; ++i)
830 this._refreshRequest(request.redirects[i]);
833 this._refreshRequest(request);
837 * @param {!WebInspector.Event} event
839 _onRequestUpdated: function(event)
841 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
842 this._refreshRequest(request);
846 * @param {!WebInspector.NetworkRequest} request
848 _refreshRequest: function(request)
850 if (!this._requestsById[request.requestId])
853 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Domain, request.domain);
854 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Method, request.requestMethod);
855 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.MimeType, request.mimeType);
856 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.StatusCode, "" + request.statusCode);
858 var responseHeaders = request.responseHeaders;
859 for (var i = 0, l = responseHeaders.length; i < l; ++i)
860 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.HasResponseHeader, responseHeaders[i].name);
861 var cookies = request.responseCookies;
862 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
863 var cookie = cookies[i];
864 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieDomain, cookie.domain());
865 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieName, cookie.name());
866 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieValue, cookie.value());
869 this._staleRequests[request.requestId] = request;
870 this._scheduleRefresh();
873 _willReloadPage: function(event)
875 this._recordButton.toggled = true;
876 if (!this._preserveLogCheckbox.checked())
881 * @param {!WebInspector.Event} event
883 _mainFrameNavigated: function(event)
885 if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
888 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
889 var loaderId = frame.loaderId;
891 // Pick provisional load requests.
892 var requestsToPick = [];
893 var requests = frame.target().networkLog.requests;
894 for (var i = 0; i < requests.length; ++i) {
895 var request = requests[i];
896 if (request.loaderId === loaderId)
897 requestsToPick.push(request);
902 for (var i = 0; i < requestsToPick.length; ++i)
903 this._appendRequest(requestsToPick[i]);
906 switchToDetailedView: function()
910 if (this._dataGrid.selectedNode)
911 this._dataGrid.selectedNode.selected = false;
913 this.element.classList.remove("brief-mode");
914 this._detailedMode = true;
915 this._updateColumns();
918 switchToBriefView: function()
920 this.element.classList.add("brief-mode");
921 this._removeAllNodeHighlights();
922 this._detailedMode = false;
923 this._updateColumns();
924 this._popoverHelper.hidePopover();
927 _toggleLargerRequests: function()
929 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
930 this._setLargerRequests(WebInspector.settings.resourcesLargeRows.get());
933 _setLargerRequests: function(enabled)
935 this._largerRequestsButton.toggled = enabled;
937 this._largerRequestsButton.title = WebInspector.UIString("Use large resource rows.");
938 this._dataGrid.element.classList.add("small");
939 this._timelineGrid.element.classList.add("small");
941 this._largerRequestsButton.title = WebInspector.UIString("Use small resource rows.");
942 this._dataGrid.element.classList.remove("small");
943 this._timelineGrid.element.classList.remove("small");
945 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
948 _getPopoverAnchor: function(element)
950 if (!this._allowPopover)
952 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
953 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
955 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
956 if (anchor && anchor.request && anchor.request.initiator)
963 * @param {!Element} anchor
964 * @param {!WebInspector.Popover} popover
966 _showPopover: function(anchor, popover)
969 if (anchor.classList.contains("network-script-initiated"))
970 content = this._generateScriptInitiatedPopoverContent(anchor.request);
972 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
973 popover.show(content, anchor);
976 _onHidePopover: function()
978 this._linkifier.reset();
982 * @param {!WebInspector.NetworkRequest} request
985 _generateScriptInitiatedPopoverContent: function(request)
987 var stackTrace = request.initiator.stackTrace;
988 var framesTable = document.createElement("table");
989 for (var i = 0; i < stackTrace.length; ++i) {
990 var stackFrame = stackTrace[i];
991 var row = document.createElement("tr");
992 row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
993 row.createChild("td").textContent = " @ ";
994 row.createChild("td").appendChild(this._linkifier.linkifyLocation(request.target(), stackFrame.url, stackFrame.lineNumber - 1, stackFrame.columnNumber - 1));
995 framesTable.appendChild(row);
1000 _updateColumns: function()
1002 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1003 var detailedMode = !!this._detailedMode;
1004 for (var columnIdentifier in columnsVisibility) {
1005 var visible = detailedMode && columnsVisibility[columnIdentifier];
1006 this._dataGrid.setColumnVisible(columnIdentifier, visible);
1008 this._dataGrid.setColumnVisible("timeline", detailedMode);
1009 this._dataGrid.applyColumnWeights();
1013 * @param {string} columnIdentifier
1015 _toggleColumnVisibility: function(columnIdentifier)
1017 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1018 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
1019 this._coulmnsVisibilitySetting.set(columnsVisibility);
1021 this._updateColumns();
1025 * @return {!Array.<string>}
1027 _getConfigurableColumnIDs: function()
1029 if (this._configurableColumnIDs)
1030 return this._configurableColumnIDs;
1032 var columns = this._dataGrid.columns;
1033 function compare(id1, id2)
1035 return columns[id1].title.compareTo(columns[id2].title);
1038 var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
1039 this._configurableColumnIDs = columnIDs.sort(compare);
1040 return this._configurableColumnIDs;
1043 _contextMenu: function(event)
1045 var contextMenu = new WebInspector.ContextMenu(event);
1047 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
1048 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1049 var columnIDs = this._getConfigurableColumnIDs();
1050 for (var i = 0; i < columnIDs.length; ++i) {
1051 var columnIdentifier = columnIDs[i];
1052 var column = this._dataGrid.columns[columnIdentifier];
1053 contextMenu.appendCheckboxItem(column.title, this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1059 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1060 var request = gridNode && gridNode._request;
1063 * @param {string} url
1065 function openResourceInNewTab(url)
1067 InspectorFrontendHost.openInNewTab(url);
1071 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), openResourceInNewTab.bind(null, request.url));
1072 contextMenu.appendSeparator();
1073 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1074 if (request.requestHeadersText())
1075 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1076 if (request.responseHeadersText)
1077 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1078 if (request.finished)
1079 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response" : "Copy Response"), this._copyResponse.bind(this, request));
1080 contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1082 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1084 contextMenu.appendSeparator();
1085 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1087 contextMenu.appendSeparator();
1088 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1089 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1091 if (request && request.type === WebInspector.resourceTypes.XHR) {
1092 contextMenu.appendSeparator();
1093 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
1094 contextMenu.appendSeparator();
1100 _replayXHR: function(requestId)
1102 NetworkAgent.replayXHR(requestId);
1105 _harRequests: function()
1107 var httpRequests = this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1108 httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1109 return httpRequests.filter(WebInspector.NetworkLogView.NonSourceMapRequestsFilter);
1112 _copyAll: function()
1115 log: (new WebInspector.HARLog(this._harRequests())).build()
1117 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1120 _copyLocation: function(request)
1122 InspectorFrontendHost.copyText(request.url);
1125 _copyRequestHeaders: function(request)
1127 InspectorFrontendHost.copyText(request.requestHeadersText());
1130 _copyResponse: function(request)
1132 function callback(content)
1134 if (request.contentEncoded)
1135 content = request.asDataURL();
1136 InspectorFrontendHost.copyText(content || "");
1138 request.requestContent(callback);
1141 _copyResponseHeaders: function(request)
1143 InspectorFrontendHost.copyText(request.responseHeadersText);
1147 * @param {!WebInspector.NetworkRequest} request
1149 _copyCurlCommand: function(request)
1151 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1154 _exportAll: function()
1156 var filename = WebInspector.resourceTreeModel.inspectedPageDomain() + ".har";
1157 var stream = new WebInspector.FileOutputStream();
1158 stream.open(filename, openCallback.bind(this));
1161 * @param {boolean} accepted
1162 * @this {WebInspector.NetworkLogView}
1164 function openCallback(accepted)
1168 var progressIndicator = new WebInspector.ProgressIndicator();
1169 this._progressBarContainer.appendChild(progressIndicator.element);
1170 var harWriter = new WebInspector.HARWriter();
1171 harWriter.write(stream, this._harRequests(), progressIndicator);
1175 _clearBrowserCache: function()
1177 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1178 NetworkAgent.clearBrowserCache();
1181 _clearBrowserCookies: function()
1183 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1184 NetworkAgent.clearBrowserCookies();
1187 _matchRequest: function(request)
1189 if (!this._searchRegExp)
1192 if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp))
1195 if (request.requestId in this._matchedRequestsMap)
1196 return this._matchedRequestsMap[request.requestId];
1198 var matchedRequestIndex = this._matchedRequests.length;
1199 this._matchedRequestsMap[request.requestId] = matchedRequestIndex;
1200 this._matchedRequests.push(request.requestId);
1202 return matchedRequestIndex;
1205 _clearSearchMatchedList: function()
1207 delete this._searchRegExp;
1208 this._matchedRequests = [];
1209 this._matchedRequestsMap = {};
1210 this._removeAllHighlights();
1213 _updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
1215 var requestIndex = this._matchedRequestsMap[oldRequestId];
1217 delete this._matchedRequestsMap[oldRequestId];
1218 this._matchedRequestsMap[newRequestId] = requestIndex;
1219 this._matchedRequests[requestIndex] = newRequestId;
1223 _updateHighlightIfMatched: function(request)
1225 var matchedRequestIndex = this._matchRequest(request);
1226 if (matchedRequestIndex === -1)
1229 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1231 if (this._currentMatchedRequestIndex !== -1 && this._currentMatchedRequestIndex !== matchedRequestIndex)
1234 this._highlightNthMatchedRequestForSearch(matchedRequestIndex, false);
1237 _removeAllHighlights: function()
1239 this._removeAllNodeHighlights();
1240 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1241 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1242 this._highlightedSubstringChanges = [];
1246 * @param {!WebInspector.NetworkRequest} request
1247 * @param {boolean} reveal
1248 * @param {!RegExp=} regExp
1250 _highlightMatchedRequest: function(request, reveal, regExp)
1252 var node = this._requestGridNode(request);
1256 var nameMatched = request.name().match(regExp);
1257 var pathMatched = request.path().match(regExp);
1258 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1259 this._toggleLargerRequests();
1260 var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
1261 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1264 this._highlightNode(node);
1269 * @param {number} matchedRequestIndex
1270 * @param {boolean} reveal
1272 _highlightNthMatchedRequestForSearch: function(matchedRequestIndex, reveal)
1274 var request = this._requestsById[this._matchedRequests[matchedRequestIndex]];
1277 this._removeAllHighlights();
1278 this._highlightMatchedRequest(request, reveal, this._searchRegExp);
1279 var node = this._requestGridNode(request);
1281 this._currentMatchedRequestIndex = matchedRequestIndex;
1283 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedRequestIndex);
1287 * @param {string} query
1288 * @param {boolean} shouldJump
1289 * @param {boolean=} jumpBackwards
1291 performSearch: function(query, shouldJump, jumpBackwards)
1293 var newMatchedRequestIndex = jumpBackwards ? -1 : 0;
1294 var currentMatchedRequestId;
1295 if (this._currentMatchedRequestIndex !== -1)
1296 currentMatchedRequestId = this._matchedRequests[this._currentMatchedRequestIndex];
1298 this._clearSearchMatchedList();
1299 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1301 var childNodes = this._dataGrid.dataTableBody.childNodes;
1302 var requestNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
1304 for (var i = 0; i < requestNodes.length; ++i) {
1305 var dataGridNode = this._dataGrid.dataGridNodeFromNode(requestNodes[i]);
1306 if (dataGridNode.isFilteredOut())
1308 if (this._matchRequest(dataGridNode._request) !== -1 && dataGridNode._request.requestId === currentMatchedRequestId) {
1309 // Keep current search result the same if it still matches.
1310 newMatchedRequestIndex = this._matchedRequests.length - 1;
1314 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1316 var index = this._normalizeSearchResultIndex(newMatchedRequestIndex);
1317 this._highlightNthMatchedRequestForSearch(index, true);
1321 _normalizeSearchResultIndex: function(index)
1323 return (index + this._matchedRequests.length) % this._matchedRequests.length;
1327 * @param {!WebInspector.NetworkDataGridNode} node
1329 _applyFilter: function(node)
1331 var request = node._request;
1332 var matches = this._resourceTypeFilterUI.accept(request.type.name());
1333 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1335 for (var i = 0; matches && (i < this._filters.length); ++i)
1336 matches = this._filters[i](request);
1338 node.element.classList.toggle("filtered-out", !matches);
1340 this._filteredOutRequests.remove(request);
1342 this._filteredOutRequests.put(request, true);
1346 * @param {string} query
1348 _parseFilterQuery: function(query)
1350 var parsedQuery = this._suggestionBuilder.parseQuery(query);
1351 this._filters = parsedQuery.text.map(this._createTextFilter);
1352 for (var key in parsedQuery.filters) {
1353 var filterType = /** @type {!WebInspector.NetworkPanel.FilterType} */ (key);
1354 this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1359 * @param {string} text
1360 * @return {!WebInspector.NetworkLogView.Filter}
1362 _createTextFilter: function(text)
1364 var regexp = new RegExp(text.escapeForRegExp(), "i");
1365 return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1369 * @param {!WebInspector.NetworkPanel.FilterType} type
1370 * @param {string} value
1371 * @return {!WebInspector.NetworkLogView.Filter}
1373 _createFilter: function(type, value) {
1375 case WebInspector.NetworkPanel.FilterType.Domain:
1376 return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1378 case WebInspector.NetworkPanel.FilterType.HasResponseHeader:
1379 return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1381 case WebInspector.NetworkPanel.FilterType.Method:
1382 return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1384 case WebInspector.NetworkPanel.FilterType.MimeType:
1385 return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1387 case WebInspector.NetworkPanel.FilterType.SetCookieDomain:
1388 return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1390 case WebInspector.NetworkPanel.FilterType.SetCookieName:
1391 return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1393 case WebInspector.NetworkPanel.FilterType.SetCookieValue:
1394 return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1396 case WebInspector.NetworkPanel.FilterType.StatusCode:
1397 return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1399 return this._createTextFilter(type + ":" + value);
1402 _filterRequests: function()
1404 this._removeAllHighlights();
1405 this._filteredOutRequests.clear();
1407 var nodes = this._dataGrid.rootNode().children;
1408 for (var i = 0; i < nodes.length; ++i)
1409 this._applyFilter(nodes[i]);
1410 this._updateSummaryBar();
1413 jumpToPreviousSearchResult: function()
1415 if (!this._matchedRequests.length)
1417 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1418 this._highlightNthMatchedRequestForSearch(index, true);
1421 jumpToNextSearchResult: function()
1423 if (!this._matchedRequests.length)
1425 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1426 this._highlightNthMatchedRequestForSearch(index, true);
1429 searchCanceled: function()
1431 this._clearSearchMatchedList();
1432 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1435 revealAndHighlightRequest: function(request)
1437 this._removeAllNodeHighlights();
1439 var node = this._requestGridNode(request);
1441 this._dataGrid.element.focus();
1443 this._highlightNode(node);
1447 _removeAllNodeHighlights: function()
1449 if (this._highlightedNode) {
1450 this._highlightedNode.element.classList.remove("highlighted-row");
1451 delete this._highlightedNode;
1455 _highlightNode: function(node)
1457 WebInspector.runCSSAnimationOnce(node.element, "highlighted-row");
1458 this._highlightedNode = node;
1462 * @param {!WebInspector.NetworkRequest} request
1465 _generateCurlCommand: function(request)
1467 var command = ["curl"];
1468 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1469 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1471 function escapeStringWin(str)
1473 /* Replace quote by double quote (but not by \") because it is
1474 recognized by both cmd.exe and MS Crt arguments parser.
1476 Replace % by "%" because it could be expanded to an environment
1477 variable value. So %% becomes "%""%". Even if an env variable ""
1478 (2 doublequotes) is declared, the cmd.exe will not
1479 substitute it with its value.
1481 Replace each backslash with double backslash to make sure
1482 MS Crt arguments parser won't collapse them.
1484 Replace new line outside of quotes since cmd.exe doesn't let
1487 return "\"" + str.replace(/"/g, "\"\"")
1488 .replace(/%/g, "\"%\"")
1489 .replace(/\\/g, "\\\\")
1490 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1493 function escapeStringPosix(str)
1495 function escapeCharacter(x)
1497 var code = x.charCodeAt(0);
1499 // Add leading zero when needed to not care about the next character.
1500 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1502 code = code.toString(16);
1503 return "\\u" + ("0000" + code).substr(code.length, 4);
1506 if (/[^\x20-\x7E]|\'/.test(str)) {
1507 // Use ANSI-C quoting syntax.
1508 return "$\'" + str.replace(/\\/g, "\\\\")
1509 .replace(/\'/g, "\\\'")
1510 .replace(/\n/g, "\\n")
1511 .replace(/\r/g, "\\r")
1512 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1514 // Use single quote syntax.
1515 return "'" + str + "'";
1519 // cURL command expected to run on the same platform that DevTools run
1520 // (it may be different from the inspected page platform).
1521 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1523 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1525 var inferredMethod = "GET";
1527 var requestContentType = request.requestContentType();
1528 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1529 data.push("--data");
1530 data.push(escapeString(request.requestFormData));
1531 ignoredHeaders["content-length"] = true;
1532 inferredMethod = "POST";
1533 } else if (request.requestFormData) {
1534 data.push("--data-binary");
1535 data.push(escapeString(request.requestFormData));
1536 ignoredHeaders["content-length"] = true;
1537 inferredMethod = "POST";
1540 if (request.requestMethod !== inferredMethod) {
1542 command.push(request.requestMethod);
1545 var requestHeaders = request.requestHeaders();
1546 for (var i = 0; i < requestHeaders.length; i++) {
1547 var header = requestHeaders[i];
1548 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1549 if (name.toLowerCase() in ignoredHeaders)
1552 command.push(escapeString(name + ": " + header.value));
1554 command = command.concat(data);
1555 command.push("--compressed");
1556 return command.join(" ");
1559 __proto__: WebInspector.VBox.prototype
1562 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1563 WebInspector.NetworkLogView.Filter;
1566 * @param {!RegExp} regex
1567 * @param {!WebInspector.NetworkRequest} request
1570 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1572 return regex.test(request.name()) || regex.test(request.path());
1576 * @param {string} value
1577 * @param {!WebInspector.NetworkRequest} request
1580 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1582 return request.domain === value;
1586 * @param {string} value
1587 * @param {!WebInspector.NetworkRequest} request
1590 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1592 return request.responseHeaderValue(value) !== undefined;
1596 * @param {string} value
1597 * @param {!WebInspector.NetworkRequest} request
1600 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1602 return request.requestMethod === value;
1606 * @param {string} value
1607 * @param {!WebInspector.NetworkRequest} request
1610 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1612 return request.mimeType === value;
1616 * @param {string} value
1617 * @param {!WebInspector.NetworkRequest} request
1620 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1622 var cookies = request.responseCookies;
1623 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1624 if (cookies[i].domain() === value)
1631 * @param {string} value
1632 * @param {!WebInspector.NetworkRequest} request
1635 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1637 var cookies = request.responseCookies;
1638 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1639 if (cookies[i].name() === value)
1646 * @param {string} value
1647 * @param {!WebInspector.NetworkRequest} request
1650 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1652 var cookies = request.responseCookies;
1653 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1654 if (cookies[i].value() === value)
1661 * @param {string} value
1662 * @param {!WebInspector.NetworkRequest} request
1665 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1667 return ("" + request.statusCode) === value;
1671 * @param {!WebInspector.NetworkRequest} request
1674 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1676 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1680 * @param {!WebInspector.NetworkRequest} request
1683 WebInspector.NetworkLogView.NonSourceMapRequestsFilter = function(request)
1685 return !WebInspector.SourceMap.hasSourceMapRequestHeader(request);
1689 * @param {!WebInspector.NetworkRequest} request
1692 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1694 return request.finished;
1697 WebInspector.NetworkLogView.EventTypes = {
1698 ViewCleared: "ViewCleared",
1699 RowSizeChanged: "RowSizeChanged",
1700 RequestSelected: "RequestSelected",
1701 SearchCountUpdated: "SearchCountUpdated",
1702 SearchIndexUpdated: "SearchIndexUpdated"
1707 * @implements {WebInspector.ContextMenu.Provider}
1708 * @implements {WebInspector.Searchable}
1709 * @extends {WebInspector.Panel}
1711 WebInspector.NetworkPanel = function()
1713 WebInspector.Panel.call(this, "network");
1714 this.registerRequiredCSS("networkPanel.css");
1715 this._injectStyles();
1717 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1718 this._filterBar = new WebInspector.FilterBar();
1719 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1720 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1721 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1722 this._filterBar.setName("networkPanel");
1724 this._searchableView = new WebInspector.SearchableView(this);
1725 this._searchableView.show(this.element);
1726 this._contentsElement = this._searchableView.element;
1728 this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1729 this._splitView.show(this._contentsElement);
1730 this._splitView.hideMain();
1732 var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisibility;
1733 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1734 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1735 var columnsVisibility = {};
1736 for (var columnId in defaultColumnsVisibility)
1737 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1738 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1740 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1741 this._networkLogView.show(this._splitView.sidebarElement());
1743 var viewsContainerView = new WebInspector.VBox();
1744 this._viewsContainerElement = viewsContainerView.element;
1745 this._viewsContainerElement.id = "network-views";
1746 if (!this._networkLogView.useLargeRows)
1747 this._viewsContainerElement.classList.add("small");
1748 viewsContainerView.show(this._splitView.mainElement());
1750 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1751 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1752 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1753 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1754 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1756 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1757 this._closeButtonElement.id = "network-close-button";
1758 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1759 this._viewsContainerElement.appendChild(this._closeButtonElement);
1761 for (var i = 0; i < this._networkLogView.statusBarItems.length; ++i)
1762 this._panelStatusBarElement.appendChild(this._networkLogView.statusBarItems[i]);
1765 * @this {WebInspector.NetworkPanel}
1766 * @return {?WebInspector.SourceFrame}
1768 function sourceFrameGetter()
1770 return this._networkItemView.currentSourceFrame();
1772 WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1775 /** @enum {string} */
1776 WebInspector.NetworkPanel.FilterType = {
1778 HasResponseHeader: "HasResponseHeader",
1780 MimeType: "MimeType",
1781 SetCookieDomain: "SetCookieDomain",
1782 SetCookieName: "SetCookieName",
1783 SetCookieValue: "SetCookieValue",
1784 StatusCode: "StatusCode"
1787 /** @type {!Array.<string>} */
1788 WebInspector.NetworkPanel._searchKeys = Object.values(WebInspector.NetworkPanel.FilterType);
1790 WebInspector.NetworkPanel.prototype = {
1791 _onFiltersToggled: function(event)
1793 var toggled = /** @type {boolean} */ (event.data);
1794 this._filtersContainer.classList.toggle("hidden", !toggled);
1795 this.element.classList.toggle("filters-toggled", toggled);
1800 * @return {!Array.<!Element>}
1802 elementsToRestoreScrollPositionsFor: function()
1804 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1808 * @return {!WebInspector.SearchableView}
1810 searchableView: function()
1812 return this._searchableView;
1815 // FIXME: only used by the layout tests, should not be exposed.
1818 this._networkLogView._reset();
1821 handleShortcut: function(event)
1823 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1824 this._toggleGridMode();
1825 event.handled = true;
1829 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1832 wasShown: function()
1834 WebInspector.Panel.prototype.wasShown.call(this);
1839 return this._networkLogView.requests;
1843 * @param {!WebInspector.NetworkRequest} request
1845 revealAndHighlightRequest: function(request)
1847 this._toggleGridMode();
1849 this._networkLogView.revealAndHighlightRequest(request);
1852 _onViewCleared: function(event)
1854 this._closeVisibleRequest();
1855 this._toggleGridMode();
1856 this._viewsContainerElement.removeChildren();
1857 this._viewsContainerElement.appendChild(this._closeButtonElement);
1860 _onRowSizeChanged: function(event)
1862 this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1865 _onSearchCountUpdated: function(event)
1867 this._searchableView.updateSearchMatchesCount(event.data);
1870 _onSearchIndexUpdated: function(event)
1872 this._searchableView.updateCurrentMatchIndex(event.data);
1875 _onRequestSelected: function(event)
1877 this._showRequest(event.data);
1881 * @param {?WebInspector.NetworkRequest} request
1883 _showRequest: function(request)
1888 this._toggleViewingRequestMode();
1890 if (this._networkItemView) {
1891 this._networkItemView.detach();
1892 delete this._networkItemView;
1895 var view = new WebInspector.NetworkItemView(request);
1896 view.show(this._viewsContainerElement);
1897 this._networkItemView = view;
1900 _closeVisibleRequest: function()
1902 this.element.classList.remove("viewing-resource");
1904 if (this._networkItemView) {
1905 this._networkItemView.detach();
1906 delete this._networkItemView;
1910 _toggleGridMode: function()
1912 if (this._viewingRequestMode) {
1913 this._viewingRequestMode = false;
1914 this.element.classList.remove("viewing-resource");
1915 this._splitView.hideMain();
1918 this._networkLogView.switchToDetailedView();
1919 this._networkLogView.allowPopover = true;
1920 this._networkLogView._allowRequestSelection = false;
1923 _toggleViewingRequestMode: function()
1925 if (this._viewingRequestMode)
1927 this._viewingRequestMode = true;
1929 this.element.classList.add("viewing-resource");
1930 this._splitView.showBoth();
1931 this._networkLogView.allowPopover = false;
1932 this._networkLogView._allowRequestSelection = true;
1933 this._networkLogView.switchToBriefView();
1937 * @param {string} query
1938 * @param {boolean} shouldJump
1939 * @param {boolean=} jumpBackwards
1941 performSearch: function(query, shouldJump, jumpBackwards)
1943 this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
1946 jumpToPreviousSearchResult: function()
1948 this._networkLogView.jumpToPreviousSearchResult();
1951 jumpToNextSearchResult: function()
1953 this._networkLogView.jumpToNextSearchResult();
1956 searchCanceled: function()
1958 this._networkLogView.searchCanceled();
1962 * @param {!WebInspector.ContextMenu} contextMenu
1963 * @param {!Object} target
1964 * @this {WebInspector.NetworkPanel}
1966 appendApplicableItems: function(event, contextMenu, target)
1969 * @this {WebInspector.NetworkPanel}
1971 function reveal(request)
1973 WebInspector.inspectorView.setCurrentPanel(this);
1974 this.revealAndHighlightRequest(request);
1978 * @this {WebInspector.NetworkPanel}
1980 function appendRevealItem(request)
1982 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
1983 contextMenu.appendItem(revealText, reveal.bind(this, request));
1986 if (target instanceof WebInspector.Resource) {
1987 var resource = /** @type {!WebInspector.Resource} */ (target);
1988 if (resource.request)
1989 appendRevealItem.call(this, resource.request);
1992 if (target instanceof WebInspector.UISourceCode) {
1993 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
1994 var resource = WebInspector.resourceForURL(uiSourceCode.url);
1995 if (resource && resource.request)
1996 appendRevealItem.call(this, resource.request);
2000 if (!(target instanceof WebInspector.NetworkRequest))
2002 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2003 if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2006 appendRevealItem.call(this, request);
2009 _injectStyles: function()
2011 var style = document.createElement("style");
2014 var columns = WebInspector.NetworkLogView._defaultColumnsVisibility;
2016 var hideSelectors = [];
2017 var bgSelectors = [];
2018 for (var columnId in columns) {
2019 hideSelectors.push("#network-container .hide-" + columnId + "-column ." + columnId + "-column");
2020 bgSelectors.push(".network-log-grid.data-grid td." + columnId + "-column");
2022 rules.push(hideSelectors.join(", ") + "{border-left: 0 none transparent;}");
2023 rules.push(bgSelectors.join(", ") + "{background-color: rgba(0, 0, 0, 0.07);}");
2025 style.textContent = rules.join("\n");
2026 document.head.appendChild(style);
2029 __proto__: WebInspector.Panel.prototype
2034 * @implements {WebInspector.ContextMenu.Provider}
2036 WebInspector.NetworkPanel.ContextMenuProvider = function()
2040 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2042 * @param {!WebInspector.ContextMenu} contextMenu
2043 * @param {!Object} target
2045 appendApplicableItems: function(event, contextMenu, target)
2047 WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2053 * @implements {WebInspector.Revealer}
2055 WebInspector.NetworkPanel.RequestRevealer = function()
2059 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2061 * @param {!Object} request
2063 reveal: function(request)
2065 if (request instanceof WebInspector.NetworkRequest)
2066 /** @type {!WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network")).revealAndHighlightRequest(request);
2072 * @implements {WebInspector.TimelineGrid.Calculator}
2074 WebInspector.NetworkBaseCalculator = function()
2078 WebInspector.NetworkBaseCalculator.prototype = {
2080 * @param {number} time
2083 computePosition: function(time)
2085 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2089 * @return {!{start: number, middle: number, end: number}}
2091 computeBarGraphPercentages: function(item)
2093 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
2097 * @return {!{left: string, right: string, tooltip: string}}
2099 computeBarGraphLabels: function(item)
2101 const label = this.formatTime(this._value(item));
2102 return {left: label, right: label, tooltip: label};
2108 boundarySpan: function()
2110 return this._maximumBoundary - this._minimumBoundary;
2116 updateBoundaries: function(item)
2118 this._minimumBoundary = 0;
2120 var value = this._value(item);
2121 if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
2122 this._maximumBoundary = value;
2130 delete this._minimumBoundary;
2131 delete this._maximumBoundary;
2137 maximumBoundary: function()
2139 return this._maximumBoundary;
2145 minimumBoundary: function()
2147 return this._minimumBoundary;
2153 zeroTime: function()
2155 return this._minimumBoundary;
2161 _value: function(item)
2167 * @param {number} value
2168 * @param {number=} precision
2171 formatTime: function(value, precision)
2173 return value.toString();
2176 setDisplayWindow: function(clientWidth)
2178 this._workingArea = clientWidth;
2184 paddingLeft: function()
2192 * @extends {WebInspector.NetworkBaseCalculator}
2194 WebInspector.NetworkTimeCalculator = function(startAtZero)
2196 WebInspector.NetworkBaseCalculator.call(this);
2197 this.startAtZero = startAtZero;
2200 WebInspector.NetworkTimeCalculator.prototype = {
2202 * @param {!WebInspector.NetworkRequest} request
2203 * @return {!{start: number, middle: number, end: number}}
2205 computeBarGraphPercentages: function(request)
2207 if (request.startTime !== -1)
2208 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2212 if (request.responseReceivedTime !== -1)
2213 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2215 var middle = (this.startAtZero ? start : 100);
2217 if (request.endTime !== -1)
2218 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2220 var end = (this.startAtZero ? middle : 100);
2222 if (this.startAtZero) {
2228 return {start: start, middle: middle, end: end};
2234 computePercentageFromEventTime: function(eventTime)
2236 // This function computes a percentage in terms of the total loading time
2237 // of a specific event. If startAtZero is set, then this is useless, and we
2238 // want to return 0.
2239 if (eventTime !== -1 && !this.startAtZero)
2240 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2248 updateBoundariesForEventTime: function(eventTime)
2250 if (eventTime === -1 || this.startAtZero)
2253 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2254 this._maximumBoundary = eventTime;
2261 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2263 computeBarGraphLabels: function(request)
2265 var rightLabel = "";
2266 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2267 rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2269 var hasLatency = request.latency > 0;
2271 var leftLabel = Number.secondsToString(request.latency);
2273 var leftLabel = rightLabel;
2276 return {left: leftLabel, right: rightLabel};
2278 if (hasLatency && rightLabel) {
2279 var total = Number.secondsToString(request.duration);
2280 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
2281 } else if (hasLatency)
2282 var tooltip = WebInspector.UIString("%s latency", leftLabel);
2283 else if (rightLabel)
2284 var tooltip = WebInspector.UIString("%s download", rightLabel);
2287 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
2288 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2294 updateBoundaries: function(request)
2296 var didChange = false;
2299 if (this.startAtZero)
2302 lowerBound = this._lowerBound(request);
2304 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2305 this._minimumBoundary = lowerBound;
2309 var upperBound = this._upperBound(request);
2310 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2311 this._maximumBoundary = upperBound;
2321 formatTime: function(value)
2323 return Number.secondsToString(value);
2326 _lowerBound: function(request)
2331 _upperBound: function(request)
2336 __proto__: WebInspector.NetworkBaseCalculator.prototype
2341 * @extends {WebInspector.NetworkTimeCalculator}
2343 WebInspector.NetworkTransferTimeCalculator = function()
2345 WebInspector.NetworkTimeCalculator.call(this, false);
2348 WebInspector.NetworkTransferTimeCalculator.prototype = {
2350 * @param {number} value
2353 formatTime: function(value)
2355 return Number.secondsToString(value - this.zeroTime());
2358 _lowerBound: function(request)
2360 return request.startTime;
2363 _upperBound: function(request)
2365 return request.endTime;
2368 __proto__: WebInspector.NetworkTimeCalculator.prototype
2373 * @extends {WebInspector.NetworkTimeCalculator}
2375 WebInspector.NetworkTransferDurationCalculator = function()
2377 WebInspector.NetworkTimeCalculator.call(this, true);
2380 WebInspector.NetworkTransferDurationCalculator.prototype = {
2382 * @param {number} value
2385 formatTime: function(value)
2387 return Number.secondsToString(value);
2390 _upperBound: function(request)
2392 return request.duration;
2395 __proto__: WebInspector.NetworkTimeCalculator.prototype
2400 * @extends {WebInspector.DataGridNode}
2401 * @param {!WebInspector.NetworkLogView} parentView
2402 * @param {!WebInspector.NetworkRequest} request
2404 WebInspector.NetworkDataGridNode = function(parentView, request)
2406 WebInspector.DataGridNode.call(this, {});
2407 this._parentView = parentView;
2408 this._request = request;
2409 this._linkifier = new WebInspector.Linkifier();
2412 WebInspector.NetworkDataGridNode.prototype = {
2414 createCells: function()
2416 this._nameCell = this._createDivInTD("name");
2417 this._methodCell = this._createDivInTD("method");
2418 this._statusCell = this._createDivInTD("status");
2419 this._schemeCell = this._createDivInTD("scheme");
2420 this._domainCell = this._createDivInTD("domain");
2421 this._remoteAddressCell = this._createDivInTD("remoteAddress");
2422 this._typeCell = this._createDivInTD("type");
2423 this._initiatorCell = this._createDivInTD("initiator");
2424 this._cookiesCell = this._createDivInTD("cookies");
2425 this._setCookiesCell = this._createDivInTD("setCookies");
2426 this._sizeCell = this._createDivInTD("size");
2427 this._timeCell = this._createDivInTD("time");
2429 this._responseHeaderCells = {};
2430 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2431 for (var i = 0; i < responseHeaderColumns.length; ++i)
2432 this._responseHeaderCells[responseHeaderColumns[i]] = this._createDivInTD(responseHeaderColumns[i]);
2434 this._timelineCell = this._createDivInTD("timeline");
2435 this._createTimelineBar(this._timelineCell);
2436 this._nameCell.addEventListener("click", this._onClick.bind(this), false);
2437 this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2440 wasDetached: function()
2442 this._linkifier.reset();
2448 isFilteredOut: function()
2450 return !!this._parentView._filteredOutRequests.get(this._request);
2453 _onClick: function()
2455 if (!this._parentView._allowRequestSelection)
2461 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2462 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
2464 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2465 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2466 url: this._request.url
2470 _highlightMatchedSubstring: function(regexp)
2472 var domChanges = [];
2473 var matchInfo = this._element.textContent.match(regexp);
2475 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2479 _openInNewTab: function()
2481 InspectorFrontendHost.openInNewTab(this._request.url);
2486 return this._parentView._allowRequestSelection && !this.isFilteredOut();
2489 _createDivInTD: function(columnIdentifier)
2491 var td = this.createTD(columnIdentifier);
2492 var div = td.createChild("div");
2493 this._element.appendChild(td);
2498 * @param {!Element} cell
2500 _createTimelineBar: function(cell)
2502 cell.className = "network-graph-side";
2504 this._barAreaElement = document.createElement("div");
2505 // this._barAreaElement.className = "network-graph-bar-area hidden";
2506 this._barAreaElement.className = "network-graph-bar-area";
2507 this._barAreaElement.request = this._request;
2508 cell.appendChild(this._barAreaElement);
2510 this._barLeftElement = document.createElement("div");
2511 this._barLeftElement.className = "network-graph-bar waiting";
2512 this._barAreaElement.appendChild(this._barLeftElement);
2514 this._barRightElement = document.createElement("div");
2515 this._barRightElement.className = "network-graph-bar";
2516 this._barAreaElement.appendChild(this._barRightElement);
2519 this._labelLeftElement = document.createElement("div");
2520 this._labelLeftElement.className = "network-graph-label waiting";
2521 this._barAreaElement.appendChild(this._labelLeftElement);
2523 this._labelRightElement = document.createElement("div");
2524 this._labelRightElement.className = "network-graph-label";
2525 this._barAreaElement.appendChild(this._labelRightElement);
2527 cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2530 refreshRequest: function()
2532 this._refreshNameCell();
2533 this._refreshMethodCell();
2534 this._refreshStatusCell();
2535 this._refreshSchemeCell();
2536 this._refreshDomainCell();
2537 this._refreshRemoteAddressCell();
2538 this._refreshTypeCell();
2539 this._refreshInitiatorCell();
2540 this._refreshCookiesCell();
2541 this._refreshSetCookiesCell();
2542 this._refreshSizeCell();
2543 this._refreshTimeCell();
2545 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2546 for (var i = 0; i < responseHeaderColumns.length; ++i)
2547 this._refreshResponseHeaderCell(responseHeaderColumns[i]);
2549 if (this._request.cached)
2550 this._timelineCell.classList.add("resource-cached");
2552 this._element.classList.add("network-item");
2553 this._element.classList.toggle("network-error-row", this._isFailed());
2554 this._updateElementStyleClasses(this._element);
2560 _isFailed: function()
2562 return !!this._request.failed || (this._request.statusCode >= 400);
2566 * @param {!Element} element
2568 _updateElementStyleClasses: function(element)
2570 var typeClassName = "network-type-" + this._request.type.name();
2571 if (!element.classList.contains(typeClassName)) {
2572 element.removeMatchingStyleClasses("network-type-\\w+");
2573 element.classList.add(typeClassName);
2577 _refreshResponseHeaderCell: function(headerName)
2579 var cell = this._responseHeaderCells[headerName];
2580 var value = this._request.responseHeaderValue(headerName);
2581 cell.setTextAndTitle(value ? value : "");
2584 _refreshNameCell: function()
2586 this._nameCell.removeChildren();
2588 if (this._request.type === WebInspector.resourceTypes.Image) {
2589 var previewImage = document.createElement("img");
2590 previewImage.className = "image-network-icon-preview";
2591 this._request.populateImageSource(previewImage);
2593 var iconElement = document.createElement("div");
2594 iconElement.className = "icon";
2595 iconElement.appendChild(previewImage);
2597 var iconElement = document.createElement("img");
2598 iconElement.className = "icon";
2600 this._nameCell.appendChild(iconElement);
2601 this._nameCell.appendChild(document.createTextNode(this._request.name()));
2602 this._appendSubtitle(this._nameCell, this._request.path());
2603 this._nameCell.title = this._request.url;
2606 _refreshMethodCell: function()
2608 this._methodCell.setTextAndTitle(this._request.requestMethod);
2611 _refreshStatusCell: function()
2613 this._statusCell.removeChildren();
2614 this._statusCell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2616 if (this._request.failed && !this._request.canceled) {
2617 var failText = WebInspector.UIString("(failed)");
2618 if (this._request.localizedFailDescription) {
2619 this._statusCell.appendChild(document.createTextNode(failText));
2620 this._appendSubtitle(this._statusCell, this._request.localizedFailDescription);
2621 this._statusCell.title = failText + " " + this._request.localizedFailDescription;
2623 this._statusCell.setTextAndTitle(failText);
2624 } else if (this._request.statusCode) {
2625 this._statusCell.appendChild(document.createTextNode("" + this._request.statusCode));
2626 this._appendSubtitle(this._statusCell, this._request.statusText);
2627 this._statusCell.title = this._request.statusCode + " " + this._request.statusText;
2628 } else if (this._request.parsedURL.isDataURL()) {
2629 this._statusCell.setTextAndTitle(WebInspector.UIString("(data)"));
2630 } else if (this._request.isPingRequest()) {
2631 this._statusCell.setTextAndTitle(WebInspector.UIString("(ping)"));
2632 } else if (this._request.canceled) {
2633 this._statusCell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2634 } else if (this._request.finished) {
2635 this._statusCell.setTextAndTitle(WebInspector.UIString("Finished"));
2637 this._statusCell.setTextAndTitle(WebInspector.UIString("(pending)"));
2641 _refreshSchemeCell: function()
2643 this._schemeCell.setTextAndTitle(this._request.scheme);
2646 _refreshDomainCell: function()
2648 this._domainCell.setTextAndTitle(this._request.domain);
2651 _refreshRemoteAddressCell: function()
2653 this._remoteAddressCell.setTextAndTitle(this._request.remoteAddress());
2656 _refreshTypeCell: function()
2658 if (this._request.mimeType) {
2659 this._typeCell.classList.remove("network-dim-cell");
2660 this._typeCell.setTextAndTitle(this._request.mimeType);
2662 this._typeCell.classList.toggle("network-dim-cell", !this._request.isPingRequest());
2663 this._typeCell.setTextAndTitle(this._request.requestContentType() || "");
2667 _refreshInitiatorCell: function()
2669 this._initiatorCell.removeChildren();
2670 this._initiatorCell.classList.remove("network-dim-cell");
2671 this._initiatorCell.classList.remove("network-script-initiated");
2672 delete this._initiatorCell.request;
2674 var request = this._request;
2675 var initiator = request.initiatorInfo();
2677 switch (initiator.type) {
2678 case WebInspector.NetworkRequest.InitiatorType.Parser:
2679 this._initiatorCell.title = initiator.url + ":" + initiator.lineNumber;
2680 this._initiatorCell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2681 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Parser"));
2684 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2685 this._initiatorCell.title = initiator.url;
2686 console.assert(request.redirectSource);
2687 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2688 this._initiatorCell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2689 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Redirect"));
2692 case WebInspector.NetworkRequest.InitiatorType.Script:
2693 var urlElement = this._linkifier.linkifyLocation(request.target(), initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2694 urlElement.title = "";
2695 this._initiatorCell.appendChild(urlElement);
2696 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
2697 this._initiatorCell.classList.add("network-script-initiated");
2698 this._initiatorCell.request = request;
2702 this._initiatorCell.title = "";
2703 this._initiatorCell.classList.add("network-dim-cell");
2704 this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
2708 _refreshCookiesCell: function()
2710 var requestCookies = this._request.requestCookies;
2711 this._cookiesCell.setTextAndTitle(requestCookies ? "" + requestCookies.length : "");
2714 _refreshSetCookiesCell: function()
2716 var responseCookies = this._request.responseCookies;
2717 this._setCookiesCell.setTextAndTitle(responseCookies ? "" + responseCookies.length : "");
2720 _refreshSizeCell: function()
2722 if (this._request.cached) {
2723 this._sizeCell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2724 this._sizeCell.classList.add("network-dim-cell");
2726 var resourceSize = Number.bytesToString(this._request.resourceSize);
2727 var transferSize = Number.bytesToString(this._request.transferSize);
2728 this._sizeCell.setTextAndTitle(transferSize);
2729 this._sizeCell.classList.remove("network-dim-cell");
2730 this._appendSubtitle(this._sizeCell, resourceSize);
2734 _refreshTimeCell: function()
2736 if (this._request.duration > 0) {
2737 this._timeCell.classList.remove("network-dim-cell");
2738 this._timeCell.setTextAndTitle(Number.secondsToString(this._request.duration));
2739 this._appendSubtitle(this._timeCell, Number.secondsToString(this._request.latency));
2741 this._timeCell.classList.add("network-dim-cell");
2742 this._timeCell.setTextAndTitle(WebInspector.UIString("Pending"));
2746 _appendSubtitle: function(cellElement, subtitleText)
2748 var subtitleElement = document.createElement("div");
2749 subtitleElement.className = "network-cell-subtitle";
2750 subtitleElement.textContent = subtitleText;
2751 cellElement.appendChild(subtitleElement);
2754 refreshGraph: function(calculator)
2756 var percentages = calculator.computeBarGraphPercentages(this._request);
2757 this._percentages = percentages;
2759 this._barAreaElement.classList.remove("hidden");
2760 this._updateElementStyleClasses(this._timelineCell);
2762 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2763 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2765 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
2766 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2768 var labels = calculator.computeBarGraphLabels(this._request);
2769 this._labelLeftElement.textContent = labels.left;
2770 this._labelRightElement.textContent = labels.right;
2772 var tooltip = (labels.tooltip || "");
2773 this._barLeftElement.title = tooltip;
2774 this._labelLeftElement.title = tooltip;
2775 this._labelRightElement.title = tooltip;
2776 this._barRightElement.title = tooltip;
2779 _refreshLabelPositions: function()
2781 if (!this._percentages)
2783 this._labelLeftElement.style.removeProperty("left");
2784 this._labelLeftElement.style.removeProperty("right");
2785 this._labelLeftElement.classList.remove("before");
2786 this._labelLeftElement.classList.remove("hidden");
2788 this._labelRightElement.style.removeProperty("left");
2789 this._labelRightElement.style.removeProperty("right");
2790 this._labelRightElement.classList.remove("after");
2791 this._labelRightElement.classList.remove("hidden");
2793 const labelPadding = 10;
2794 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2795 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2797 if (this._barLeftElement) {
2798 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2799 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2801 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2802 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2805 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2806 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2808 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2809 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2810 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2812 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2813 var leftHidden = true;
2815 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2816 var rightHidden = true;
2818 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2819 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2820 if (labelBefore && !labelAfter)
2822 else if (labelAfter && !labelBefore)
2828 this._labelLeftElement.classList.add("hidden");
2829 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2830 this._labelLeftElement.classList.add("before");
2832 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2833 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2838 this._labelRightElement.classList.add("hidden");
2839 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2840 this._labelRightElement.classList.add("after");
2842 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2843 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2847 __proto__: WebInspector.DataGridNode.prototype
2850 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2852 var aFileName = a._request.name();
2853 var bFileName = b._request.name();
2854 if (aFileName > bFileName)
2856 if (bFileName > aFileName)
2861 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2863 var aRemoteAddress = a._request.remoteAddress();
2864 var bRemoteAddress = b._request.remoteAddress();
2865 if (aRemoteAddress > bRemoteAddress)
2867 if (bRemoteAddress > aRemoteAddress)
2872 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2874 if (b._request.cached && !a._request.cached)
2876 if (a._request.cached && !b._request.cached)
2879 return a._request.transferSize - b._request.transferSize;
2882 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2884 var aInitiator = a._request.initiatorInfo();
2885 var bInitiator = b._request.initiatorInfo();
2887 if (aInitiator.type < bInitiator.type)
2889 if (aInitiator.type > bInitiator.type)
2892 if (aInitiator.source < bInitiator.source)
2894 if (aInitiator.source > bInitiator.source)
2897 if (aInitiator.lineNumber < bInitiator.lineNumber)
2899 if (aInitiator.lineNumber > bInitiator.lineNumber)
2902 if (aInitiator.columnNumber < bInitiator.columnNumber)
2904 if (aInitiator.columnNumber > bInitiator.columnNumber)
2910 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
2912 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
2913 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
2914 return aScore - bScore;
2917 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
2919 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
2920 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
2921 return aScore - bScore;
2924 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
2926 var aValue = a._request[propertyName];
2927 var bValue = b._request[propertyName];
2928 if (aValue > bValue)
2929 return revert ? -1 : 1;
2930 if (bValue > aValue)
2931 return revert ? 1 : -1;