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("networkLogView.css");
43 this.registerRequiredCSS("filter.css");
45 this._filterBar = filterBar;
46 this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
47 this._allowRequestSelection = false;
48 /** @type {!StringMap.<!WebInspector.NetworkDataGridNode>} */
49 this._nodesByRequestId = new StringMap();
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._recordButton.toggled = 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",
103 MimeType: "MimeType",
105 SetCookieDomain: "SetCookieDomain",
106 SetCookieName: "SetCookieName",
107 SetCookieValue: "SetCookieValue",
108 StatusCode: "StatusCode"
111 /** @type {!Array.<string>} */
112 WebInspector.NetworkLogView._searchKeys = Object.values(WebInspector.NetworkLogView.FilterType);
114 /** @type {!Object.<string, string>} */
115 WebInspector.NetworkLogView._columnTitles = {
116 "name": WebInspector.UIString("Name"),
117 "method": WebInspector.UIString("Method"),
118 "status": WebInspector.UIString("Status"),
119 "scheme": WebInspector.UIString("Scheme"),
120 "domain": WebInspector.UIString("Domain"),
121 "remoteAddress": WebInspector.UIString("Remote Address"),
122 "type": WebInspector.UIString("Type"),
123 "initiator": WebInspector.UIString("Initiator"),
124 "cookies": WebInspector.UIString("Cookies"),
125 "setCookies": WebInspector.UIString("Set-Cookies"),
126 "size": WebInspector.UIString("Size"),
127 "time": WebInspector.UIString("Time"),
128 "connectionId": WebInspector.UIString("Connection Id"),
129 "timeline": WebInspector.UIString("Timeline"),
131 // Response header columns
132 "Cache-Control": WebInspector.UIString("Cache-Control"),
133 "Connection": WebInspector.UIString("Connection"),
134 "Content-Encoding": WebInspector.UIString("Content-Encoding"),
135 "Content-Length": WebInspector.UIString("Content-Length"),
136 "ETag": WebInspector.UIString("ETag"),
137 "Keep-Alive": WebInspector.UIString("Keep-Alive"),
138 "Last-Modified": WebInspector.UIString("Last-Modified"),
139 "Server": WebInspector.UIString("Server"),
140 "Vary": WebInspector.UIString("Vary")
143 WebInspector.NetworkLogView.prototype = {
145 * @param {!WebInspector.Target} target
147 targetAdded: function(target)
149 target.networkLog.requests.forEach(this._appendRequest.bind(this));
153 * @param {!WebInspector.Target} target
155 targetRemoved: function(target)
162 allowRequestSelection: function()
164 return this._allowRequestSelection;
167 _addFilters: function()
169 this._textFilterUI = new WebInspector.TextFilterUI();
170 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
171 this._filterBar.addFilter(this._textFilterUI);
174 for (var typeId in WebInspector.resourceTypes) {
175 var resourceType = WebInspector.resourceTypes[typeId];
176 types.push({name: resourceType.name(), label: resourceType.categoryTitle()});
178 this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI(types, WebInspector.settings.networkResourceTypeFilters);
179 this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
180 this._filterBar.addFilter(this._resourceTypeFilterUI);
182 var dataURLSetting = WebInspector.settings.networkHideDataURL;
183 this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
184 this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
185 this._filterBar.addFilter(this._dataURLFilterUI);
188 _resetSuggestionBuilder: function()
190 this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkLogView._searchKeys);
191 this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
195 * @param {!WebInspector.Event} event
197 _filterChanged: function(event)
199 this._removeAllNodeHighlights();
200 this._parseFilterQuery(this._textFilterUI.value());
201 this._filterRequests();
204 _initializeView: function()
206 this.element.id = "network-container";
208 this._createSortingFunctions();
209 this._createCalculators();
211 this._createTimelineGrid();
212 this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
214 this._updateRowsSize();
216 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
217 // Enable faster hint.
218 this._popoverHelper.setTimeout(100);
220 this.switchViewMode(true);
224 * @return {!Array.<!Element>}
226 statusBarItems: function()
229 this._recordButton.element,
230 this._clearButton.element,
231 this._filterBar.filterButton().element,
232 this._largerRequestsButton.element,
233 this._preserveLogCheckbox.element,
234 this._disableCacheCheckbox.element,
235 this._progressBarContainer];
241 usesLargeRows: function()
243 return !!WebInspector.settings.resourcesLargeRows.get();
247 * @param {boolean} flag
249 setAllowPopover: function(flag)
251 this._allowPopover = flag;
255 * @return {!Array.<!Element>}
257 elementsToRestoreScrollPositionsFor: function()
259 if (!this._dataGrid) // Not initialized yet.
261 return [this._dataGrid.scrollContainer];
264 _createTimelineGrid: function()
266 this._timelineGrid = new WebInspector.TimelineGrid();
267 this._timelineGrid.element.classList.add("network-timeline-grid");
268 this._dataGrid.element.appendChild(this._timelineGrid.element);
271 _createTable: function()
276 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
277 title: WebInspector.NetworkLogView._columnTitles["name"],
285 title: WebInspector.NetworkLogView._columnTitles["method"],
292 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
293 title: WebInspector.NetworkLogView._columnTitles["status"],
300 title: WebInspector.NetworkLogView._columnTitles["scheme"],
307 title: WebInspector.NetworkLogView._columnTitles["domain"],
314 title: WebInspector.NetworkLogView._columnTitles["remoteAddress"],
317 align: WebInspector.DataGrid.Align.Right
322 title: WebInspector.NetworkLogView._columnTitles["type"],
329 title: WebInspector.NetworkLogView._columnTitles["initiator"],
336 title: WebInspector.NetworkLogView._columnTitles["cookies"],
339 align: WebInspector.DataGrid.Align.Right
344 title: WebInspector.NetworkLogView._columnTitles["setCookies"],
347 align: WebInspector.DataGrid.Align.Right
352 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
353 title: WebInspector.NetworkLogView._columnTitles["size"],
356 align: WebInspector.DataGrid.Align.Right
361 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
362 title: WebInspector.NetworkLogView._columnTitles["time"],
365 align: WebInspector.DataGrid.Align.Right
370 title: WebInspector.NetworkLogView._columnTitles["connectionId"],
375 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
376 for (var i = 0; i < responseHeaderColumns.length; ++i) {
377 var headerName = responseHeaderColumns[i];
380 title: WebInspector.NetworkLogView._columnTitles[headerName],
383 if (headerName === "Content-Length")
384 descriptor.align = WebInspector.DataGrid.Align.Right;
385 columns.push(descriptor);
390 titleDOMFragment: document.createDocumentFragment(),
391 title: WebInspector.NetworkLogView._columnTitles["timeline"],
394 sort: WebInspector.DataGrid.Order.Ascending
397 this._dataGrid = new WebInspector.SortableDataGrid(columns);
398 this._dataGrid.setStickToBottom(true);
399 this._updateColumns();
400 this._dataGrid.setName("networkLog");
401 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
402 this._dataGrid.element.classList.add("network-log-grid");
403 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
404 this._dataGrid.show(this.element);
406 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
407 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
408 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
410 this._patchTimelineHeader();
411 this._dataGrid.sortNodes(this._sortingFunctions.startTime, false);
415 * @param {string} title
416 * @param {string} subtitle
417 * @return {!DocumentFragment}
419 _makeHeaderFragment: function(title, subtitle)
421 var fragment = document.createDocumentFragment();
422 fragment.createTextChild(title);
423 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
424 subtitleDiv.createTextChild(subtitle);
428 _patchTimelineHeader: function()
430 var timelineSorting = document.createElement("select");
432 var option = document.createElement("option");
433 option.value = "startTime";
434 option.label = WebInspector.UIString("Timeline");
435 timelineSorting.appendChild(option);
437 option = document.createElement("option");
438 option.value = "startTime";
439 option.label = WebInspector.UIString("Start Time");
440 timelineSorting.appendChild(option);
442 option = document.createElement("option");
443 option.value = "responseTime";
444 option.label = WebInspector.UIString("Response Time");
445 timelineSorting.appendChild(option);
447 option = document.createElement("option");
448 option.value = "endTime";
449 option.label = WebInspector.UIString("End Time");
450 timelineSorting.appendChild(option);
452 option = document.createElement("option");
453 option.value = "duration";
454 option.label = WebInspector.UIString("Duration");
455 timelineSorting.appendChild(option);
457 option = document.createElement("option");
458 option.value = "latency";
459 option.label = WebInspector.UIString("Latency");
460 timelineSorting.appendChild(option);
462 var header = this._dataGrid.headerTableHeader("timeline");
463 header.replaceChild(timelineSorting, header.firstChild);
465 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
466 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
467 this._timelineSortSelector = timelineSorting;
470 _createSortingFunctions: function()
472 this._sortingFunctions = {};
473 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
474 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
475 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
476 this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
477 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
478 this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
479 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
480 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
481 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
482 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
483 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
484 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
485 this._sortingFunctions.connectionId = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "connectionId", false);
486 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
487 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
488 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
489 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
490 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
491 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
494 _createCalculators: function()
496 /** @type {!WebInspector.NetworkTransferTimeCalculator} */
497 this._timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
498 /** @type {!WebInspector.NetworkTransferDurationCalculator} */
499 this._durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
501 /** @type {!Object.<string, !WebInspector.NetworkTimeCalculator>} */
502 this._calculators = {};
503 this._calculators.timeline = this._timeCalculator;
504 this._calculators.startTime = this._timeCalculator;
505 this._calculators.endTime = this._timeCalculator;
506 this._calculators.responseTime = this._timeCalculator;
507 this._calculators.duration = this._durationCalculator;
508 this._calculators.latency = this._durationCalculator;
510 this._calculator = this._timeCalculator;
513 _sortItems: function()
515 this._removeAllNodeHighlights();
516 var columnIdentifier = this._dataGrid.sortColumnIdentifier();
517 if (columnIdentifier === "timeline") {
518 this._sortByTimeline();
521 var sortingFunction = this._sortingFunctions[columnIdentifier];
522 if (!sortingFunction)
525 this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
526 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
527 this._timelineSortSelector.selectedIndex = 0;
529 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
530 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
531 column: columnIdentifier,
532 sortOrder: this._dataGrid.sortOrder()
536 _sortByTimeline: function()
538 this._removeAllNodeHighlights();
539 var selectedIndex = this._timelineSortSelector.selectedIndex;
541 selectedIndex = 1; // Sort by start time by default.
542 var selectedOption = this._timelineSortSelector[selectedIndex];
543 var value = selectedOption.value;
545 this._setCalculator(this._calculators[value]);
546 var sortingFunction = this._sortingFunctions[value];
547 this._dataGrid.sortNodes(sortingFunction);
548 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
549 this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
552 _createStatusBarItems: function()
554 this._progressBarContainer = document.createElement("div");
555 this._progressBarContainer.className = "status-bar-item";
558 _updateSummaryBar: function()
560 var requestsNumber = this._nodesByRequestId.size;
562 if (!requestsNumber) {
563 if (this._summaryBarElement._isDisplayingWarning)
565 this._summaryBarElement._isDisplayingWarning = true;
566 this._summaryBarElement.removeChildren();
567 this._summaryBarElement.createChild("div", "warning-icon-small");
568 var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
569 this._summaryBarElement.createTextChild(text);
570 this._summaryBarElement.title = text;
573 delete this._summaryBarElement._isDisplayingWarning;
575 var transferSize = 0;
576 var selectedRequestsNumber = 0;
577 var selectedTransferSize = 0;
580 var nodes = this._nodesByRequestId.values();
581 for (var i = 0; i < nodes.length; ++i) {
582 var request = nodes[i].request();
583 var requestTransferSize = request.transferSize;
584 transferSize += requestTransferSize;
585 if (!nodes[i]._isFilteredOut) {
586 selectedRequestsNumber++;
587 selectedTransferSize += requestTransferSize;
589 if (request.url === request.target().resourceTreeModel.inspectedPageURL())
590 baseTime = request.startTime;
591 if (request.endTime > maxTime)
592 maxTime = request.endTime;
595 if (selectedRequestsNumber !== requestsNumber) {
596 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
597 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
599 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
600 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
602 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
603 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
604 Number.secondsToString(maxTime - baseTime),
605 Number.secondsToString(this._mainRequestLoadTime - baseTime),
606 Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
608 this._summaryBarElement.textContent = text;
609 this._summaryBarElement.title = text;
612 _scheduleRefresh: function()
614 if (this._needsRefresh)
617 this._needsRefresh = true;
619 if (this.isShowing() && !this._refreshTimeout)
620 this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
623 _updateDividersIfNeeded: function()
625 var timelineOffset = this._dataGrid.columnOffset("timeline");
626 // Position timline grid location.
628 this._timelineGrid.element.style.left = timelineOffset + "px";
630 var calculator = this.calculator();
632 if (!this.isShowing()) {
633 this._scheduleRefresh();
636 calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
637 proceed = this._timelineGrid.updateDividers(calculator);
642 if (calculator.startAtZero) {
643 // If our current sorting method starts at zero, that means it shows all
644 // requests starting at the same point, and so onLoad event and DOMContent
645 // event lines really wouldn't make much sense here, so don't render them.
649 this._timelineGrid.removeEventDividers();
650 if (this._mainRequestLoadTime !== -1) {
651 var percent = calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
653 var loadDivider = document.createElement("div");
654 loadDivider.className = "network-event-divider network-red-divider";
656 var loadDividerPadding = document.createElement("div");
657 loadDividerPadding.className = "network-event-divider-padding";
658 loadDividerPadding.title = WebInspector.UIString("Load event");
659 loadDividerPadding.appendChild(loadDivider);
660 loadDividerPadding.style.left = percent + "%";
661 this._timelineGrid.addEventDivider(loadDividerPadding);
664 if (this._mainRequestDOMContentLoadedTime !== -1) {
665 var percent = calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
667 var domContentLoadedDivider = document.createElement("div");
668 domContentLoadedDivider.className = "network-event-divider network-blue-divider";
670 var domContentLoadedDividerPadding = document.createElement("div");
671 domContentLoadedDividerPadding.className = "network-event-divider-padding";
672 domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event");
673 domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
674 domContentLoadedDividerPadding.style.left = percent + "%";
675 this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
679 _refreshIfNeeded: function()
681 if (this._needsRefresh)
685 _invalidateAllItems: function()
687 var requestIds = this._nodesByRequestId.keys();
688 for (var i = 0; i < requestIds.length; ++i)
689 this._staleRequestIds[requestIds[i]] = true;
693 * @return {!WebInspector.NetworkTimeCalculator}
695 calculator: function()
697 return this._calculator;
701 * @param {!WebInspector.NetworkTimeCalculator} x
703 _setCalculator: function(x)
705 if (!x || this._calculator === x)
708 this._calculator = x;
709 this._calculator.reset();
711 if (this._calculator.startAtZero)
712 this._timelineGrid.hideEventDividers();
714 this._timelineGrid.showEventDividers();
716 this._invalidateAllItems();
720 _createStatusbarButtons: function()
722 this._recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record Network Log"), "record-profile-status-bar-item");
723 this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
725 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
726 this._clearButton.addEventListener("click", this._reset, this);
728 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
729 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
730 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
732 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
733 this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
735 this._disableCacheCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Disable cache"));
736 WebInspector.SettingsUI.bindCheckbox(this._disableCacheCheckbox.inputElement, WebInspector.settings.cacheDisabled);
737 this._disableCacheCheckbox.element.title = WebInspector.UIString("Disable cache (while DevTools is open).");
741 * @param {!WebInspector.Event} event
743 _loadEventFired: function(event)
745 if (!this._recordButton.toggled)
748 var data = /** @type {number} */ (event.data);
749 this._mainRequestLoadTime = data || -1;
750 // Schedule refresh to update boundaries and draw the new line.
751 this._scheduleRefresh();
755 * @param {!WebInspector.Event} event
757 _domContentLoadedEventFired: function(event)
759 if (!this._recordButton.toggled)
761 var data = /** @type {number} */ (event.data);
762 this._mainRequestDOMContentLoadedTime = data || -1;
763 // Schedule refresh to update boundaries and draw the new line.
764 this._scheduleRefresh();
769 this._refreshIfNeeded();
774 this._popoverHelper.hidePopover();
779 this._needsRefresh = false;
780 if (this._refreshTimeout) {
781 clearTimeout(this._refreshTimeout);
782 delete this._refreshTimeout;
785 this._removeAllNodeHighlights();
786 var boundariesChanged = false;
787 var calculator = this.calculator();
788 if (calculator.updateBoundariesForEventTime) {
789 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
790 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
793 var dataGrid = this._dataGrid;
794 var rootNode = dataGrid.rootNode();
795 var nodesToInsert = [];
796 for (var requestId in this._staleRequestIds) {
797 var node = this._nodesByRequestId.get(requestId);
800 if (!node._isFilteredOut)
801 rootNode.removeChild(node);
802 node._isFilteredOut = !this._applyFilter(node);
803 if (!node._isFilteredOut)
804 nodesToInsert.push(node);
807 for (var i = 0; i < nodesToInsert.length; ++i) {
808 var node = nodesToInsert[i];
809 var request = node.request();
811 dataGrid.insertChild(node);
812 node._isMatchingSearchQuery = this._matchRequest(request);
813 if (calculator.updateBoundaries(request))
814 boundariesChanged = true;
817 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
819 if (boundariesChanged) {
820 // The boundaries changed, so all item graphs are stale.
821 this._updateDividersIfNeeded();
822 var nodes = this._nodesByRequestId.values();
823 for (var i = 0; i < nodes.length; ++i)
824 nodes[i].refreshGraph();
827 this._staleRequestIds = {};
828 this._updateSummaryBar();
831 _onRecordButtonClicked: function()
833 if (!this._recordButton.toggled)
835 this._recordButton.toggled = !this._recordButton.toggled;
840 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
842 this._clearSearchMatchedList();
843 if (this._popoverHelper)
844 this._popoverHelper.hidePopover();
846 if (this._calculator)
847 this._calculator.reset();
849 var nodes = this._nodesByRequestId.values();
850 for (var i = 0; i < nodes.length; ++i)
853 this._nodesByRequestId.clear();
854 this._staleRequestIds = {};
855 this._resetSuggestionBuilder();
857 if (this._dataGrid) {
858 this._dataGrid.rootNode().removeChildren();
859 this._updateDividersIfNeeded();
860 this._updateSummaryBar();
863 this._mainRequestLoadTime = -1;
864 this._mainRequestDOMContentLoadedTime = -1;
868 * @param {!WebInspector.Event} event
870 _onRequestStarted: function(event)
872 if (this._recordButton.toggled) {
873 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
874 this._appendRequest(request);
879 * @param {!WebInspector.NetworkRequest} request
881 _appendRequest: function(request)
883 var node = new WebInspector.NetworkDataGridNode(this, request);
885 // In case of redirect request id is reassigned to a redirected
886 // request and we need to update _nodesByRequestId and search results.
887 var originalRequestNode = this._nodesByRequestId.get(request.requestId);
888 if (originalRequestNode)
889 this._nodesByRequestId.set(originalRequestNode.request().requestId, originalRequestNode);
890 this._nodesByRequestId.set(request.requestId, node);
892 // Pull all the redirects of the main request upon commit load.
893 if (request.redirects) {
894 for (var i = 0; i < request.redirects.length; ++i)
895 this._refreshRequest(request.redirects[i]);
898 this._refreshRequest(request);
902 * @param {!WebInspector.Event} event
904 _onRequestUpdated: function(event)
906 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
907 this._refreshRequest(request);
911 * @param {!WebInspector.NetworkRequest} request
913 _refreshRequest: function(request)
915 if (!this._nodesByRequestId.get(request.requestId))
918 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Domain, request.domain);
919 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Method, request.requestMethod);
920 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MimeType, request.mimeType);
921 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Scheme, "" + request.scheme);
922 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.StatusCode, "" + request.statusCode);
924 var responseHeaders = request.responseHeaders;
925 for (var i = 0, l = responseHeaders.length; i < l; ++i)
926 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
927 var cookies = request.responseCookies;
928 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
929 var cookie = cookies[i];
930 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
931 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieName, cookie.name());
932 this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieValue, cookie.value());
935 this._staleRequestIds[request.requestId] = true;
936 this._scheduleRefresh();
940 * @param {!WebInspector.Event} event
942 _willReloadPage: function(event)
944 this._recordButton.toggled = true;
945 if (!this._preserveLogCheckbox.checked())
950 * @param {!WebInspector.Event} event
952 _mainFrameNavigated: function(event)
954 if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
957 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
958 var loaderId = frame.loaderId;
960 // Pick provisional load requests.
961 var requestsToPick = [];
962 var requests = frame.target().networkLog.requests;
963 for (var i = 0; i < requests.length; ++i) {
964 var request = requests[i];
965 if (request.loaderId === loaderId)
966 requestsToPick.push(request);
971 for (var i = 0; i < requestsToPick.length; ++i)
972 this._appendRequest(requestsToPick[i]);
976 * @param {boolean} detailed
978 switchViewMode: function(detailed)
980 if (this._detailedMode === detailed)
982 this._detailedMode = detailed;
985 if (this._dataGrid.selectedNode)
986 this._dataGrid.selectedNode.selected = false;
988 this._removeAllNodeHighlights();
989 this._popoverHelper.hidePopover();
992 this.element.classList.toggle("brief-mode", !detailed);
993 this._updateColumns();
996 _toggleLargerRequests: function()
998 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
999 this._updateRowsSize();
1005 rowHeight: function()
1007 return this._rowHeight;
1010 _updateRowsSize: function()
1012 var largeRows = this.usesLargeRows();
1013 this._largerRequestsButton.toggled = largeRows;
1014 this._rowHeight = largeRows ? 41 : 21;
1015 this._largerRequestsButton.title = WebInspector.UIString(largeRows ? "Use small resource rows." : "Use large resource rows.");
1016 this._dataGrid.element.classList.toggle("small", !largeRows);
1017 this._timelineGrid.element.classList.toggle("small", !largeRows);
1018 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: largeRows });
1022 * @param {!Element} element
1023 * @param {!Event} event
1024 * @return {!Element|!AnchorBox|undefined}
1026 _getPopoverAnchor: function(element, event)
1028 if (!this._allowPopover)
1030 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
1031 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
1033 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
1034 if (anchor && anchor.request) {
1035 var request = /** @type {!WebInspector.NetworkRequest} */ (anchor.request);
1036 var initiator = anchor.request.initiator();
1037 if (initiator && (initiator.stackTrace || initiator.asyncStackTrace))
1043 * @param {!Element} anchor
1044 * @param {!WebInspector.Popover} popover
1046 _showPopover: function(anchor, popover)
1049 if (anchor.classList.contains("network-script-initiated"))
1050 content = this._generateScriptInitiatedPopoverContent(anchor.request);
1052 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
1053 popover.show(content, anchor);
1056 _onHidePopover: function()
1058 this._linkifier.reset();
1062 * @param {!WebInspector.NetworkRequest} request
1063 * @return {!Element}
1065 _generateScriptInitiatedPopoverContent: function(request)
1067 var framesTable = document.createElementWithClass("table", "network-stack-trace");
1070 * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
1071 * @this {WebInspector.NetworkLogView}
1073 function appendStackTrace(stackTrace)
1075 for (var i = 0; i < stackTrace.length; ++i) {
1076 var stackFrame = stackTrace[i];
1077 var row = document.createElement("tr");
1078 row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
1079 row.createChild("td").textContent = " @ ";
1080 row.createChild("td").appendChild(this._linkifier.linkifyConsoleCallFrame(request.target(), stackFrame));
1081 framesTable.appendChild(row);
1085 // Initiator is not null, checked in _getPopoverAnchor.
1086 var initiator = /** @type {!NetworkAgent.Initiator} */ (request.initiator());
1087 if (initiator.stackTrace)
1088 appendStackTrace.call(this, initiator.stackTrace);
1090 var asyncStackTrace = initiator.asyncStackTrace;
1091 while (asyncStackTrace) {
1092 var callFrames = asyncStackTrace.callFrames;
1093 if (!callFrames || !callFrames.length)
1095 var row = framesTable.createChild("tr");
1096 row.createChild("td", "network-async-trace-description").textContent = WebInspector.asyncStackTraceLabel(asyncStackTrace.description);
1097 row.createChild("td");
1098 row.createChild("td");
1099 appendStackTrace.call(this, callFrames);
1100 asyncStackTrace = asyncStackTrace.asyncStackTrace;
1106 _updateColumns: function()
1108 var detailedMode = !!this._detailedMode;
1109 var visibleColumns = {"name": true};
1111 visibleColumns["timeline"] = true;
1112 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1113 for (var columnIdentifier in columnsVisibility)
1114 visibleColumns[columnIdentifier] = columnsVisibility[columnIdentifier];
1117 this._dataGrid.setColumnsVisiblity(visibleColumns);
1121 * @param {string} columnIdentifier
1123 _toggleColumnVisibility: function(columnIdentifier)
1125 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1126 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
1127 this._coulmnsVisibilitySetting.set(columnsVisibility);
1129 this._updateColumns();
1133 * @return {!Array.<string>}
1135 _getConfigurableColumnIDs: function()
1137 if (this._configurableColumnIDs)
1138 return this._configurableColumnIDs;
1140 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1141 function compare(id1, id2)
1143 return columnTitles[id1].compareTo(columnTitles[id2]);
1146 var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
1147 this._configurableColumnIDs = columnIDs.sort(compare);
1148 return this._configurableColumnIDs;
1152 * @param {!Event} event
1154 _contextMenu: function(event)
1156 var contextMenu = new WebInspector.ContextMenu(event);
1158 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
1159 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1160 var columnIDs = this._getConfigurableColumnIDs();
1161 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1162 for (var i = 0; i < columnIDs.length; ++i) {
1163 var columnIdentifier = columnIDs[i];
1164 contextMenu.appendCheckboxItem(columnTitles[columnIdentifier], this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1170 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1171 var request = gridNode && gridNode.request();
1174 * @param {string} url
1176 function openResourceInNewTab(url)
1178 InspectorFrontendHost.openInNewTab(url);
1182 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), openResourceInNewTab.bind(null, request.url));
1183 contextMenu.appendSeparator();
1184 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1185 if (request.requestHeadersText())
1186 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1187 if (request.responseHeadersText)
1188 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1189 if (request.finished)
1190 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response" : "Copy Response"), this._copyResponse.bind(this, request));
1191 contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1193 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1195 contextMenu.appendSeparator();
1196 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1198 contextMenu.appendSeparator();
1199 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1200 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1202 if (request && request.type === WebInspector.resourceTypes.XHR) {
1203 contextMenu.appendSeparator();
1204 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), request.replayXHR.bind(request));
1205 contextMenu.appendSeparator();
1211 _harRequests: function()
1213 var requests = this._nodesByRequestId.values().map(function(node) { return node.request(); });
1214 var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1215 httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1216 return httpRequests.filter(WebInspector.NetworkLogView.NonDevToolsRequestsFilter);
1219 _copyAll: function()
1222 log: (new WebInspector.HARLog(this._harRequests())).build()
1224 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1228 * @param {!WebInspector.NetworkRequest} request
1230 _copyLocation: function(request)
1232 InspectorFrontendHost.copyText(request.url);
1236 * @param {!WebInspector.NetworkRequest} request
1238 _copyRequestHeaders: function(request)
1240 InspectorFrontendHost.copyText(request.requestHeadersText());
1244 * @param {!WebInspector.NetworkRequest} request
1246 _copyResponse: function(request)
1249 * @param {?string} content
1251 function callback(content)
1253 if (request.contentEncoded)
1254 content = request.asDataURL();
1255 InspectorFrontendHost.copyText(content || "");
1257 request.requestContent(callback);
1261 * @param {!WebInspector.NetworkRequest} request
1263 _copyResponseHeaders: function(request)
1265 InspectorFrontendHost.copyText(request.responseHeadersText);
1269 * @param {!WebInspector.NetworkRequest} request
1271 _copyCurlCommand: function(request)
1273 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1276 _exportAll: function()
1278 var filename = WebInspector.targetManager.inspectedPageDomain() + ".har";
1279 var stream = new WebInspector.FileOutputStream();
1280 stream.open(filename, openCallback.bind(this));
1283 * @param {boolean} accepted
1284 * @this {WebInspector.NetworkLogView}
1286 function openCallback(accepted)
1290 var progressIndicator = new WebInspector.ProgressIndicator();
1291 this._progressBarContainer.appendChild(progressIndicator.element);
1292 var harWriter = new WebInspector.HARWriter();
1293 harWriter.write(stream, this._harRequests(), progressIndicator);
1297 _clearBrowserCache: function()
1299 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1300 NetworkAgent.clearBrowserCache();
1303 _clearBrowserCookies: function()
1305 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1306 NetworkAgent.clearBrowserCookies();
1310 * @param {!WebInspector.NetworkRequest} request
1313 _matchRequest: function(request)
1315 var re = this._searchRegExp;
1318 return re.test(request.name()) || re.test(request.path());
1321 _clearSearchMatchedList: function()
1323 this._matchedRequestCount = -1;
1324 this._currentMatchedRequestNode = null;
1325 this._removeAllHighlights();
1328 _removeAllHighlights: function()
1330 this._removeAllNodeHighlights();
1331 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1332 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1333 this._highlightedSubstringChanges = [];
1338 * @param {boolean} reveal
1340 _highlightNthMatchedRequestForSearch: function(n, reveal)
1342 this._removeAllHighlights();
1344 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1345 var nodes = this._dataGrid.rootNode().children;
1348 for (var i = 0; i < nodes.length; ++i) {
1349 if (nodes[i]._isMatchingSearchQuery) {
1350 if (matchCount === n) {
1358 this._currentMatchedRequestNode = null;
1362 var request = node.request();
1363 var regExp = this._searchRegExp;
1364 var nameMatched = request.name().match(regExp);
1365 var pathMatched = request.path().match(regExp);
1366 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1367 this._toggleLargerRequests();
1369 WebInspector.Revealer.reveal(request);
1370 var highlightedSubstringChanges = node.highlightMatchedSubstring(regExp);
1371 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1373 this._currentMatchedRequestNode = node;
1374 this._currentMatchedRequestIndex = n;
1375 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
1379 * @param {string} query
1380 * @param {boolean} shouldJump
1381 * @param {boolean=} jumpBackwards
1383 performSearch: function(query, shouldJump, jumpBackwards)
1385 var currentMatchedRequestNode = this._currentMatchedRequestNode;
1386 this._clearSearchMatchedList();
1387 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1389 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1390 var nodes = this._dataGrid.rootNode().children;
1391 for (var i = 0; i < nodes.length; ++i)
1392 nodes[i]._isMatchingSearchQuery = this._matchRequest(nodes[i].request());
1393 var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
1394 if (!newMatchedRequestIndex && jumpBackwards)
1395 newMatchedRequestIndex = this._matchedRequestCount - 1;
1396 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
1400 * @param {?WebInspector.NetworkDataGridNode} node
1403 _updateMatchCountAndFindMatchIndex: function(node)
1405 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1406 var nodes = this._dataGrid.rootNode().children;
1409 for (var i = 0; i < nodes.length; ++i) {
1410 if (!nodes[i]._isMatchingSearchQuery)
1412 if (node === nodes[i])
1413 matchIndex = matchCount;
1416 if (this._matchedRequestCount !== matchCount) {
1417 this._matchedRequestCount = matchCount;
1418 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
1424 * @param {number} index
1427 _normalizeSearchResultIndex: function(index)
1429 return (index + this._matchedRequestCount) % this._matchedRequestCount;
1433 * @param {!WebInspector.NetworkDataGridNode} node
1436 _applyFilter: function(node)
1438 var request = node.request();
1439 if (!this._resourceTypeFilterUI.accept(request.type.name()))
1441 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1443 for (var i = 0; i < this._filters.length; ++i) {
1444 if (!this._filters[i](request))
1451 * @param {string} query
1453 _parseFilterQuery: function(query)
1455 var parsedQuery = this._suggestionBuilder.parseQuery(query);
1456 this._filters = parsedQuery.text.map(this._createTextFilter);
1457 for (var key in parsedQuery.filters) {
1458 var filterType = /** @type {!WebInspector.NetworkLogView.FilterType} */ (key);
1459 this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1464 * @param {string} text
1465 * @return {!WebInspector.NetworkLogView.Filter}
1467 _createTextFilter: function(text)
1469 var regexp = new RegExp(text.escapeForRegExp(), "i");
1470 return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1474 * @param {!WebInspector.NetworkLogView.FilterType} type
1475 * @param {string} value
1476 * @return {!WebInspector.NetworkLogView.Filter}
1478 _createFilter: function(type, value) {
1480 case WebInspector.NetworkLogView.FilterType.Domain:
1481 return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1483 case WebInspector.NetworkLogView.FilterType.HasResponseHeader:
1484 return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1486 case WebInspector.NetworkLogView.FilterType.Method:
1487 return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1489 case WebInspector.NetworkLogView.FilterType.MimeType:
1490 return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1492 case WebInspector.NetworkLogView.FilterType.Scheme:
1493 return WebInspector.NetworkLogView._requestSchemeFilter.bind(null, value);
1495 case WebInspector.NetworkLogView.FilterType.SetCookieDomain:
1496 return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1498 case WebInspector.NetworkLogView.FilterType.SetCookieName:
1499 return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1501 case WebInspector.NetworkLogView.FilterType.SetCookieValue:
1502 return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1504 case WebInspector.NetworkLogView.FilterType.StatusCode:
1505 return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1507 return this._createTextFilter(type + ":" + value);
1510 _filterRequests: function()
1512 this._removeAllHighlights();
1513 this._invalidateAllItems();
1517 jumpToPreviousSearchResult: function()
1519 if (!this._matchedRequestCount)
1521 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1522 this._highlightNthMatchedRequestForSearch(index, true);
1525 jumpToNextSearchResult: function()
1527 if (!this._matchedRequestCount)
1529 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1530 this._highlightNthMatchedRequestForSearch(index, true);
1533 searchCanceled: function()
1535 delete this._searchRegExp;
1536 this._clearSearchMatchedList();
1537 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1541 * @param {!WebInspector.NetworkRequest} request
1543 revealAndHighlightRequest: function(request)
1545 this._removeAllNodeHighlights();
1547 var node = this._nodesByRequestId.get(request.requestId);
1550 this._highlightNode(node);
1554 _removeAllNodeHighlights: function()
1556 if (this._highlightedNode) {
1557 this._highlightedNode.element().classList.remove("highlighted-row");
1558 delete this._highlightedNode;
1563 * @param {!WebInspector.NetworkDataGridNode} node
1565 _highlightNode: function(node)
1567 WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
1568 this._highlightedNode = node;
1572 * @param {!WebInspector.NetworkRequest} request
1575 _generateCurlCommand: function(request)
1577 var command = ["curl"];
1578 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1579 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1581 function escapeStringWin(str)
1583 /* Replace quote by double quote (but not by \") because it is
1584 recognized by both cmd.exe and MS Crt arguments parser.
1586 Replace % by "%" because it could be expanded to an environment
1587 variable value. So %% becomes "%""%". Even if an env variable ""
1588 (2 doublequotes) is declared, the cmd.exe will not
1589 substitute it with its value.
1591 Replace each backslash with double backslash to make sure
1592 MS Crt arguments parser won't collapse them.
1594 Replace new line outside of quotes since cmd.exe doesn't let
1597 return "\"" + str.replace(/"/g, "\"\"")
1598 .replace(/%/g, "\"%\"")
1599 .replace(/\\/g, "\\\\")
1600 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1603 function escapeStringPosix(str)
1605 function escapeCharacter(x)
1607 var code = x.charCodeAt(0);
1609 // Add leading zero when needed to not care about the next character.
1610 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1612 code = code.toString(16);
1613 return "\\u" + ("0000" + code).substr(code.length, 4);
1616 if (/[^\x20-\x7E]|\'/.test(str)) {
1617 // Use ANSI-C quoting syntax.
1618 return "$\'" + str.replace(/\\/g, "\\\\")
1619 .replace(/\'/g, "\\\'")
1620 .replace(/\n/g, "\\n")
1621 .replace(/\r/g, "\\r")
1622 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1624 // Use single quote syntax.
1625 return "'" + str + "'";
1629 // cURL command expected to run on the same platform that DevTools run
1630 // (it may be different from the inspected page platform).
1631 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1633 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1635 var inferredMethod = "GET";
1637 var requestContentType = request.requestContentType();
1638 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1639 data.push("--data");
1640 data.push(escapeString(request.requestFormData));
1641 ignoredHeaders["content-length"] = true;
1642 inferredMethod = "POST";
1643 } else if (request.requestFormData) {
1644 data.push("--data-binary");
1645 data.push(escapeString(request.requestFormData));
1646 ignoredHeaders["content-length"] = true;
1647 inferredMethod = "POST";
1650 if (request.requestMethod !== inferredMethod) {
1652 command.push(request.requestMethod);
1655 var requestHeaders = request.requestHeaders();
1656 for (var i = 0; i < requestHeaders.length; i++) {
1657 var header = requestHeaders[i];
1658 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1659 if (name.toLowerCase() in ignoredHeaders)
1662 command.push(escapeString(name + ": " + header.value));
1664 command = command.concat(data);
1665 command.push("--compressed");
1666 return command.join(" ");
1669 __proto__: WebInspector.VBox.prototype
1672 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1673 WebInspector.NetworkLogView.Filter;
1676 * @param {!RegExp} regex
1677 * @param {!WebInspector.NetworkRequest} request
1680 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1682 return regex.test(request.name()) || regex.test(request.path());
1686 * @param {string} value
1687 * @param {!WebInspector.NetworkRequest} request
1690 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1692 return request.domain === value;
1696 * @param {string} value
1697 * @param {!WebInspector.NetworkRequest} request
1700 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1702 return request.responseHeaderValue(value) !== undefined;
1706 * @param {string} value
1707 * @param {!WebInspector.NetworkRequest} request
1710 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1712 return request.requestMethod === value;
1716 * @param {string} value
1717 * @param {!WebInspector.NetworkRequest} request
1720 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1722 return request.mimeType === value;
1726 * @param {string} value
1727 * @param {!WebInspector.NetworkRequest} request
1730 WebInspector.NetworkLogView._requestSchemeFilter = function(value, request)
1732 return request.scheme === value;
1736 * @param {string} value
1737 * @param {!WebInspector.NetworkRequest} request
1740 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1742 var cookies = request.responseCookies;
1743 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1744 if (cookies[i].domain() === value)
1751 * @param {string} value
1752 * @param {!WebInspector.NetworkRequest} request
1755 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1757 var cookies = request.responseCookies;
1758 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1759 if (cookies[i].name() === value)
1766 * @param {string} value
1767 * @param {!WebInspector.NetworkRequest} request
1770 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1772 var cookies = request.responseCookies;
1773 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1774 if (cookies[i].value() === value)
1781 * @param {string} value
1782 * @param {!WebInspector.NetworkRequest} request
1785 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1787 return ("" + request.statusCode) === value;
1791 * @param {!WebInspector.NetworkRequest} request
1794 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1796 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1800 * @param {!WebInspector.NetworkRequest} request
1803 WebInspector.NetworkLogView.NonDevToolsRequestsFilter = function(request)
1805 return !WebInspector.NetworkManager.hasDevToolsRequestHeader(request);
1809 * @param {!WebInspector.NetworkRequest} request
1812 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1814 return request.finished;
1817 WebInspector.NetworkLogView.EventTypes = {
1818 ViewCleared: "ViewCleared",
1819 RowSizeChanged: "RowSizeChanged",
1820 RequestSelected: "RequestSelected",
1821 SearchCountUpdated: "SearchCountUpdated",
1822 SearchIndexUpdated: "SearchIndexUpdated"
1827 * @implements {WebInspector.ContextMenu.Provider}
1828 * @implements {WebInspector.Searchable}
1829 * @extends {WebInspector.Panel}
1831 WebInspector.NetworkPanel = function()
1833 WebInspector.Panel.call(this, "network");
1834 this.registerRequiredCSS("networkPanel.css");
1836 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1837 this._filterBar = new WebInspector.FilterBar();
1838 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1839 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1840 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1841 this._filterBar.setName("networkPanel");
1843 this._searchableView = new WebInspector.SearchableView(this);
1844 this._searchableView.show(this.element);
1845 var contentsElement = this._searchableView.element;
1847 this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1848 this._splitView.show(contentsElement);
1849 this._splitView.hideMain();
1851 var defaultColumnsVisibility = WebInspector.NetworkLogView.defaultColumnsVisibility;
1852 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1853 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1854 var columnsVisibility = {};
1855 for (var columnId in defaultColumnsVisibility)
1856 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1857 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1859 /** @type {!WebInspector.NetworkLogView} */
1860 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1861 this._networkLogView.show(this._splitView.sidebarElement());
1863 var viewsContainerView = new WebInspector.VBox();
1864 this._viewsContainerElement = viewsContainerView.element;
1865 this._viewsContainerElement.id = "network-views";
1866 if (!this._networkLogView.usesLargeRows())
1867 this._viewsContainerElement.classList.add("small");
1868 viewsContainerView.show(this._splitView.mainElement());
1870 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1871 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1872 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1873 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1874 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1876 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1877 this._closeButtonElement.id = "network-close-button";
1878 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1879 this._viewsContainerElement.appendChild(this._closeButtonElement);
1881 var statusBarItems = this._networkLogView.statusBarItems();
1882 for (var i = 0; i < statusBarItems.length; ++i)
1883 this._panelStatusBarElement.appendChild(statusBarItems[i]);
1886 * @this {WebInspector.NetworkPanel}
1887 * @return {?WebInspector.SourceFrame}
1889 function sourceFrameGetter()
1891 return this._networkItemView.currentSourceFrame();
1893 WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1896 WebInspector.NetworkPanel.prototype = {
1898 * @param {!WebInspector.Event} event
1900 _onFiltersToggled: function(event)
1902 var toggled = /** @type {boolean} */ (event.data);
1903 this._filtersContainer.classList.toggle("hidden", !toggled);
1904 this.element.classList.toggle("filters-toggled", toggled);
1909 * @return {!Array.<!Element>}
1911 elementsToRestoreScrollPositionsFor: function()
1913 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1917 * @return {!WebInspector.SearchableView}
1919 searchableView: function()
1921 return this._searchableView;
1925 * @param {!KeyboardEvent} event
1927 handleShortcut: function(event)
1929 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1930 this._toggleGridMode();
1931 event.handled = true;
1935 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1938 wasShown: function()
1940 WebInspector.Panel.prototype.wasShown.call(this);
1944 * @param {!WebInspector.NetworkRequest} request
1946 revealAndHighlightRequest: function(request)
1948 this._toggleGridMode();
1950 this._networkLogView.revealAndHighlightRequest(request);
1954 * @param {!WebInspector.Event} event
1956 _onViewCleared: function(event)
1958 this._closeVisibleRequest();
1959 this._toggleGridMode();
1960 this._viewsContainerElement.removeChildren();
1961 this._viewsContainerElement.appendChild(this._closeButtonElement);
1965 * @param {!WebInspector.Event} event
1967 _onRowSizeChanged: function(event)
1969 this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1973 * @param {!WebInspector.Event} event
1975 _onSearchCountUpdated: function(event)
1977 var count = /** @type {number} */ (event.data);
1978 this._searchableView.updateSearchMatchesCount(count);
1982 * @param {!WebInspector.Event} event
1984 _onSearchIndexUpdated: function(event)
1986 var index = /** @type {number} */ (event.data);
1987 this._searchableView.updateCurrentMatchIndex(index);
1991 * @param {!WebInspector.Event} event
1993 _onRequestSelected: function(event)
1995 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
1996 this._showRequest(request);
2000 * @param {?WebInspector.NetworkRequest} request
2002 _showRequest: function(request)
2007 this._toggleViewingRequestMode();
2009 if (this._networkItemView) {
2010 this._networkItemView.detach();
2011 delete this._networkItemView;
2014 var view = new WebInspector.NetworkItemView(request);
2015 view.show(this._viewsContainerElement);
2016 this._networkItemView = view;
2019 _closeVisibleRequest: function()
2021 this.element.classList.remove("viewing-resource");
2023 if (this._networkItemView) {
2024 this._networkItemView.detach();
2025 delete this._networkItemView;
2029 _toggleGridMode: function()
2031 if (this._viewingRequestMode) {
2032 this._viewingRequestMode = false;
2033 this.element.classList.remove("viewing-resource");
2034 this._splitView.hideMain();
2037 this._networkLogView.switchViewMode(true);
2038 this._networkLogView.setAllowPopover(true);
2039 this._networkLogView._allowRequestSelection = false;
2042 _toggleViewingRequestMode: function()
2044 if (this._viewingRequestMode)
2046 this._viewingRequestMode = true;
2048 this.element.classList.add("viewing-resource");
2049 this._splitView.showBoth();
2050 this._networkLogView.setAllowPopover(false);
2051 this._networkLogView._allowRequestSelection = true;
2052 this._networkLogView.switchViewMode(false);
2056 * @param {string} query
2057 * @param {boolean} shouldJump
2058 * @param {boolean=} jumpBackwards
2060 performSearch: function(query, shouldJump, jumpBackwards)
2062 this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
2065 jumpToPreviousSearchResult: function()
2067 this._networkLogView.jumpToPreviousSearchResult();
2070 jumpToNextSearchResult: function()
2072 this._networkLogView.jumpToNextSearchResult();
2075 searchCanceled: function()
2077 this._networkLogView.searchCanceled();
2081 * @param {!Event} event
2082 * @param {!WebInspector.ContextMenu} contextMenu
2083 * @param {!Object} target
2084 * @this {WebInspector.NetworkPanel}
2086 appendApplicableItems: function(event, contextMenu, target)
2089 * @this {WebInspector.NetworkPanel}
2091 function reveal(request)
2093 WebInspector.inspectorView.setCurrentPanel(this);
2094 this.revealAndHighlightRequest(request);
2098 * @this {WebInspector.NetworkPanel}
2100 function appendRevealItem(request)
2102 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
2103 contextMenu.appendItem(revealText, reveal.bind(this, request));
2106 if (target instanceof WebInspector.Resource) {
2107 var resource = /** @type {!WebInspector.Resource} */ (target);
2108 if (resource.request)
2109 appendRevealItem.call(this, resource.request);
2112 if (target instanceof WebInspector.UISourceCode) {
2113 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
2114 var resource = WebInspector.resourceForURL(uiSourceCode.url);
2115 if (resource && resource.request)
2116 appendRevealItem.call(this, resource.request);
2120 if (!(target instanceof WebInspector.NetworkRequest))
2122 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2123 if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2126 appendRevealItem.call(this, request);
2129 __proto__: WebInspector.Panel.prototype
2134 * @implements {WebInspector.ContextMenu.Provider}
2136 WebInspector.NetworkPanel.ContextMenuProvider = function()
2140 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2142 * @param {!Event} event
2143 * @param {!WebInspector.ContextMenu} contextMenu
2144 * @param {!Object} target
2146 appendApplicableItems: function(event, contextMenu, target)
2148 WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2154 * @implements {WebInspector.Revealer}
2156 WebInspector.NetworkPanel.RequestRevealer = function()
2160 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2162 * @param {!Object} request
2163 * @param {number=} lineNumber
2165 reveal: function(request, lineNumber)
2167 if (request instanceof WebInspector.NetworkRequest) {
2168 var panel = /** @type {?WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network"));
2170 panel.revealAndHighlightRequest(request);
2177 * @implements {WebInspector.TimelineGrid.Calculator}
2179 WebInspector.NetworkTimeCalculator = function(startAtZero)
2181 this.startAtZero = startAtZero;
2184 /** @type {!WebInspector.UIStringFormat} */
2185 WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat = new WebInspector.UIStringFormat("%s latency, %s download (%s total)");
2187 /** @type {!WebInspector.UIStringFormat} */
2188 WebInspector.NetworkTimeCalculator._latencyFormat = new WebInspector.UIStringFormat("%s latency");
2190 /** @type {!WebInspector.UIStringFormat} */
2191 WebInspector.NetworkTimeCalculator._downloadFormat = new WebInspector.UIStringFormat("%s download");
2193 /** @type {!WebInspector.UIStringFormat} */
2194 WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat = new WebInspector.UIStringFormat("%s (from ServiceWorker)");
2196 /** @type {!WebInspector.UIStringFormat} */
2197 WebInspector.NetworkTimeCalculator._fromCacheFormat = new WebInspector.UIStringFormat("%s (from cache)");
2199 WebInspector.NetworkTimeCalculator.prototype = {
2204 paddingLeft: function()
2211 * @param {number} time
2214 computePosition: function(time)
2216 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2221 * @param {number} value
2222 * @param {number=} precision
2225 formatTime: function(value, precision)
2227 return Number.secondsToString(value);
2234 minimumBoundary: function()
2236 return this._minimumBoundary;
2243 zeroTime: function()
2245 return this._minimumBoundary;
2252 maximumBoundary: function()
2254 return this._maximumBoundary;
2261 boundarySpan: function()
2263 return this._maximumBoundary - this._minimumBoundary;
2268 delete this._minimumBoundary;
2269 delete this._maximumBoundary;
2275 _value: function(item)
2281 * @param {number} clientWidth
2283 setDisplayWindow: function(clientWidth)
2285 this._workingArea = clientWidth;
2289 * @param {!WebInspector.NetworkRequest} request
2290 * @return {!{start: number, middle: number, end: number}}
2292 computeBarGraphPercentages: function(request)
2294 if (request.startTime !== -1)
2295 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2299 if (request.responseReceivedTime !== -1)
2300 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2302 var middle = (this.startAtZero ? start : 100);
2304 if (request.endTime !== -1)
2305 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2307 var end = (this.startAtZero ? middle : 100);
2309 if (this.startAtZero) {
2315 return {start: start, middle: middle, end: end};
2319 * @param {number} eventTime
2322 computePercentageFromEventTime: function(eventTime)
2324 // This function computes a percentage in terms of the total loading time
2325 // of a specific event. If startAtZero is set, then this is useless, and we
2326 // want to return 0.
2327 if (eventTime !== -1 && !this.startAtZero)
2328 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2334 * @param {number} eventTime
2337 updateBoundariesForEventTime: function(eventTime)
2339 if (eventTime === -1 || this.startAtZero)
2342 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2343 this._maximumBoundary = eventTime;
2350 * @param {!WebInspector.NetworkRequest} request
2351 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2353 computeBarGraphLabels: function(request)
2355 var rightLabel = "";
2356 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2357 rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2359 var hasLatency = request.latency > 0;
2361 var leftLabel = Number.secondsToString(request.latency);
2363 var leftLabel = rightLabel;
2366 return {left: leftLabel, right: rightLabel};
2368 if (hasLatency && rightLabel) {
2369 var total = Number.secondsToString(request.duration);
2370 var tooltip = WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat.format(leftLabel, rightLabel, total);
2371 } else if (hasLatency)
2372 var tooltip = WebInspector.NetworkTimeCalculator._latencyFormat.format(leftLabel);
2373 else if (rightLabel)
2374 var tooltip = WebInspector.NetworkTimeCalculator._downloadFormat.format(rightLabel);
2376 if (request.fetchedViaServiceWorker)
2377 tooltip = WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat.format(tooltip);
2378 else if (request.cached)
2379 tooltip = WebInspector.NetworkTimeCalculator._fromCacheFormat.format(tooltip);
2380 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2384 * @param {!WebInspector.NetworkRequest} request
2387 updateBoundaries: function(request)
2389 var didChange = false;
2392 if (this.startAtZero)
2395 lowerBound = this._lowerBound(request);
2397 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2398 this._minimumBoundary = lowerBound;
2402 var upperBound = this._upperBound(request);
2403 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2404 this._maximumBoundary = upperBound;
2412 * @param {!WebInspector.NetworkRequest} request
2415 _lowerBound: function(request)
2421 * @param {!WebInspector.NetworkRequest} request
2424 _upperBound: function(request)
2432 * @extends {WebInspector.NetworkTimeCalculator}
2434 WebInspector.NetworkTransferTimeCalculator = function()
2436 WebInspector.NetworkTimeCalculator.call(this, false);
2439 WebInspector.NetworkTransferTimeCalculator.prototype = {
2442 * @param {number} value
2443 * @param {number=} precision
2446 formatTime: function(value, precision)
2448 return Number.secondsToString(value - this.zeroTime());
2453 * @param {!WebInspector.NetworkRequest} request
2456 _lowerBound: function(request)
2458 return request.startTime;
2463 * @param {!WebInspector.NetworkRequest} request
2466 _upperBound: function(request)
2468 return request.endTime;
2471 __proto__: WebInspector.NetworkTimeCalculator.prototype
2476 * @extends {WebInspector.NetworkTimeCalculator}
2478 WebInspector.NetworkTransferDurationCalculator = function()
2480 WebInspector.NetworkTimeCalculator.call(this, true);
2483 WebInspector.NetworkTransferDurationCalculator.prototype = {
2486 * @param {number} value
2487 * @param {number=} precision
2490 formatTime: function(value, precision)
2492 return Number.secondsToString(value);
2497 * @param {!WebInspector.NetworkRequest} request
2500 _upperBound: function(request)
2502 return request.duration;
2505 __proto__: WebInspector.NetworkTimeCalculator.prototype
2510 * @extends {WebInspector.SortableDataGridNode}
2511 * @param {!WebInspector.NetworkLogView} parentView
2512 * @param {!WebInspector.NetworkRequest} request
2514 WebInspector.NetworkDataGridNode = function(parentView, request)
2516 WebInspector.SortableDataGridNode.call(this, {});
2517 this._parentView = parentView;
2518 this._request = request;
2519 this._linkifier = new WebInspector.Linkifier();
2520 this._isFilteredOut = true;
2521 this._isMatchingSearchQuery = false;
2522 this._staleGraph = true;
2525 WebInspector.NetworkDataGridNode.prototype = {
2527 * @return {!WebInspector.NetworkRequest}
2531 return this._request;
2538 nodeSelfHeight: function() {
2539 return this._parentView.rowHeight();
2543 createCells: function()
2545 this._nameCell = null;
2546 this._timelineCell = null;
2548 this._element.classList.toggle("network-error-row", this._isFailed());
2549 WebInspector.SortableDataGridNode.prototype.createCells.call(this);
2551 this._updateGraph();
2556 * @param {string} columnIdentifier
2557 * @return {!Element}
2559 createCell: function(columnIdentifier)
2561 var cell = this.createTD(columnIdentifier);
2562 switch (columnIdentifier) {
2563 case "name": this._renderNameCell(cell); break;
2564 case "timeline": this._createTimelineBar(cell); break;
2565 case "method": cell.setTextAndTitle(this._request.requestMethod); break;
2566 case "status": this._renderStatusCell(cell); break;
2567 case "scheme": cell.setTextAndTitle(this._request.scheme); break;
2568 case "domain": cell.setTextAndTitle(this._request.domain); break;
2569 case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
2570 case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
2571 case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
2572 case "connectionId": cell.setTextAndTitle(this._request.connectionId); break;
2573 case "type": cell.setTextAndTitle(this._request.mimeType || this._request.requestContentType() || ""); break;
2574 case "initiator": this._renderInitiatorCell(cell); break;
2575 case "size": this._renderSizeCell(cell); break;
2576 case "time": this._renderTimeCell(cell); break;
2577 default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
2584 * @param {?Array} array
2587 _arrayLength: function(array)
2589 return array ? "" + array.length : "";
2596 willAttach: function()
2598 if (this._staleGraph)
2599 this._updateGraph();
2602 wasDetached: function()
2604 if (this._linkifiedInitiatorAnchor)
2605 this._linkifiedInitiatorAnchor.remove();
2610 this._linkifier.reset();
2613 _onClick: function()
2615 if (!this._parentView.allowRequestSelection())
2621 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2622 WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
2624 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2625 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2626 url: this._request.url
2631 * @param {!RegExp=} regexp
2632 * @return {!Array.<!Object>}
2634 highlightMatchedSubstring: function(regexp)
2636 // Ensure element is created.
2638 var domChanges = [];
2639 var matchInfo = this._nameCell.textContent.match(regexp);
2641 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2645 _openInNewTab: function()
2647 InspectorFrontendHost.openInNewTab(this._request.url);
2652 return this._parentView.allowRequestSelection();
2656 * @param {!Element} cell
2658 _createTimelineBar: function(cell)
2660 cell = cell.createChild("div");
2661 this._timelineCell = cell;
2663 cell.className = "network-graph-side";
2665 this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
2666 this._barAreaElement.request = this._request;
2668 var type = this._request.type.name();
2669 var cached = this._request.cached;
2671 this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
2672 this._barLeftElement.classList.add(type, "waiting");
2673 this._barLeftElement.classList.toggle("cached", cached);
2675 this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
2676 this._barRightElement.classList.add(type);
2677 this._barRightElement.classList.toggle("cached", cached);
2679 this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
2680 this._labelLeftElement.classList.add("waiting");
2682 this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
2684 cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2690 _isFailed: function()
2692 return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
2696 * @param {!Element} cell
2698 _renderNameCell: function(cell)
2700 this._nameCell = cell;
2701 cell.addEventListener("click", this._onClick.bind(this), false);
2702 cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2704 if (this._request.type === WebInspector.resourceTypes.Image) {
2705 var previewImage = document.createElementWithClass("img", "image-network-icon-preview");
2706 this._request.populateImageSource(previewImage);
2708 iconElement = document.createElementWithClass("div", "icon");
2709 iconElement.appendChild(previewImage);
2711 iconElement = document.createElementWithClass("img", "icon");
2713 iconElement.classList.add(this._request.type.name());
2715 cell.appendChild(iconElement);
2716 cell.createTextChild(this._request.name());
2717 this._appendSubtitle(cell, this._request.path());
2718 cell.title = this._request.url;
2722 * @param {!Element} cell
2724 _renderStatusCell: function(cell)
2726 cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2728 if (this._request.failed && !this._request.canceled) {
2729 var failText = WebInspector.UIString("(failed)");
2730 if (this._request.localizedFailDescription) {
2731 cell.createTextChild(failText);
2732 this._appendSubtitle(cell, this._request.localizedFailDescription);
2733 cell.title = failText + " " + this._request.localizedFailDescription;
2735 cell.setTextAndTitle(failText);
2736 } else if (this._request.statusCode) {
2737 cell.createTextChild("" + this._request.statusCode);
2738 this._appendSubtitle(cell, this._request.statusText);
2739 cell.title = this._request.statusCode + " " + this._request.statusText;
2740 } else if (this._request.parsedURL.isDataURL()) {
2741 cell.setTextAndTitle(WebInspector.UIString("(data)"));
2742 } else if (this._request.canceled) {
2743 cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2744 } else if (this._request.finished) {
2745 cell.setTextAndTitle(WebInspector.UIString("Finished"));
2747 cell.setTextAndTitle(WebInspector.UIString("(pending)"));
2752 * @param {!Element} cell
2754 _renderInitiatorCell: function(cell)
2756 var request = this._request;
2757 var initiator = request.initiatorInfo();
2759 switch (initiator.type) {
2760 case WebInspector.NetworkRequest.InitiatorType.Parser:
2761 cell.title = initiator.url + ":" + initiator.lineNumber;
2762 cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2763 this._appendSubtitle(cell, WebInspector.UIString("Parser"));
2766 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2767 cell.title = initiator.url;
2768 console.assert(request.redirectSource);
2769 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2770 cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2771 this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
2774 case WebInspector.NetworkRequest.InitiatorType.Script:
2775 if (!this._linkifiedInitiatorAnchor) {
2776 this._linkifiedInitiatorAnchor = this._linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2777 this._linkifiedInitiatorAnchor.title = "";
2779 cell.appendChild(this._linkifiedInitiatorAnchor);
2780 this._appendSubtitle(cell, WebInspector.UIString("Script"));
2781 cell.classList.add("network-script-initiated");
2782 cell.request = request;
2787 cell.classList.add("network-dim-cell");
2788 cell.setTextAndTitle(WebInspector.UIString("Other"));
2793 * @param {!Element} cell
2795 _renderSizeCell: function(cell)
2797 if (this._request.fetchedViaServiceWorker) {
2798 cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)"));
2799 cell.classList.add("network-dim-cell");
2800 } else if (this._request.cached) {
2801 cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2802 cell.classList.add("network-dim-cell");
2804 var resourceSize = Number.bytesToString(this._request.resourceSize);
2805 var transferSize = Number.bytesToString(this._request.transferSize);
2806 cell.setTextAndTitle(transferSize);
2807 this._appendSubtitle(cell, resourceSize);
2812 * @param {!Element} cell
2814 _renderTimeCell: function(cell)
2816 if (this._request.duration > 0) {
2817 cell.setTextAndTitle(Number.secondsToString(this._request.duration));
2818 this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
2820 cell.classList.add("network-dim-cell");
2821 cell.setTextAndTitle(WebInspector.UIString("Pending"));
2826 * @param {!Element} cellElement
2827 * @param {string} subtitleText
2829 _appendSubtitle: function(cellElement, subtitleText)
2831 var subtitleElement = document.createElement("div");
2832 subtitleElement.className = "network-cell-subtitle";
2833 subtitleElement.textContent = subtitleText;
2834 cellElement.appendChild(subtitleElement);
2837 refreshGraph: function()
2839 if (!this._timelineCell)
2841 this._staleGraph = true;
2842 if (this.attached())
2843 this.dataGrid.scheduleUpdate();
2846 _updateGraph: function()
2848 this._staleGraph = false;
2849 if (!this._timelineCell)
2852 var calculator = this._parentView.calculator();
2853 var percentages = calculator.computeBarGraphPercentages(this._request);
2854 this._percentages = percentages;
2856 this._barAreaElement.classList.remove("hidden");
2858 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2859 this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
2861 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2862 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2864 var labels = calculator.computeBarGraphLabels(this._request);
2865 this._labelLeftElement.textContent = labels.left;
2866 this._labelRightElement.textContent = labels.right;
2868 var tooltip = (labels.tooltip || "");
2869 this._barLeftElement.title = tooltip;
2870 this._labelLeftElement.title = tooltip;
2871 this._labelRightElement.title = tooltip;
2872 this._barRightElement.title = tooltip;
2875 _refreshLabelPositions: function()
2877 if (!this._percentages)
2879 this._labelLeftElement.style.removeProperty("left");
2880 this._labelLeftElement.style.removeProperty("right");
2881 this._labelLeftElement.classList.remove("before");
2882 this._labelLeftElement.classList.remove("hidden");
2884 this._labelRightElement.style.removeProperty("left");
2885 this._labelRightElement.style.removeProperty("right");
2886 this._labelRightElement.classList.remove("after");
2887 this._labelRightElement.classList.remove("hidden");
2889 const labelPadding = 10;
2890 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2891 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2893 if (this._barLeftElement) {
2894 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2895 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2897 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2898 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2901 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2902 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2904 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2905 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2906 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2908 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2909 var leftHidden = true;
2911 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2912 var rightHidden = true;
2914 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2915 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2916 if (labelBefore && !labelAfter)
2918 else if (labelAfter && !labelBefore)
2924 this._labelLeftElement.classList.add("hidden");
2925 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2926 this._labelLeftElement.classList.add("before");
2928 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2929 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2934 this._labelRightElement.classList.add("hidden");
2935 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2936 this._labelRightElement.classList.add("after");
2938 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2939 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2943 __proto__: WebInspector.SortableDataGridNode.prototype
2947 * @param {!WebInspector.NetworkDataGridNode} a
2948 * @param {!WebInspector.NetworkDataGridNode} b
2951 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2953 var aFileName = a._request.name();
2954 var bFileName = b._request.name();
2955 if (aFileName > bFileName)
2957 if (bFileName > aFileName)
2959 return a._request.indentityCompare(b._request);
2963 * @param {!WebInspector.NetworkDataGridNode} a
2964 * @param {!WebInspector.NetworkDataGridNode} b
2967 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2969 var aRemoteAddress = a._request.remoteAddress();
2970 var bRemoteAddress = b._request.remoteAddress();
2971 if (aRemoteAddress > bRemoteAddress)
2973 if (bRemoteAddress > aRemoteAddress)
2975 return a._request.indentityCompare(b._request);
2979 * @param {!WebInspector.NetworkDataGridNode} a
2980 * @param {!WebInspector.NetworkDataGridNode} b
2983 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2985 if (b._request.cached && !a._request.cached)
2987 if (a._request.cached && !b._request.cached)
2989 return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
2993 * @param {!WebInspector.NetworkDataGridNode} a
2994 * @param {!WebInspector.NetworkDataGridNode} b
2997 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2999 var aInitiator = a._request.initiatorInfo();
3000 var bInitiator = b._request.initiatorInfo();
3002 if (aInitiator.type < bInitiator.type)
3004 if (aInitiator.type > bInitiator.type)
3007 if (typeof aInitiator.__source === "undefined")
3008 aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
3009 if (typeof bInitiator.__source === "undefined")
3010 bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
3012 if (aInitiator.__source < bInitiator.__source)
3014 if (aInitiator.__source > bInitiator.__source)
3017 if (aInitiator.lineNumber < bInitiator.lineNumber)
3019 if (aInitiator.lineNumber > bInitiator.lineNumber)
3022 if (aInitiator.columnNumber < bInitiator.columnNumber)
3024 if (aInitiator.columnNumber > bInitiator.columnNumber)
3027 return a._request.indentityCompare(b._request);
3031 * @param {!WebInspector.NetworkDataGridNode} a
3032 * @param {!WebInspector.NetworkDataGridNode} b
3035 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
3037 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
3038 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
3039 return (aScore - bScore) || a._request.indentityCompare(b._request);
3043 * @param {!WebInspector.NetworkDataGridNode} a
3044 * @param {!WebInspector.NetworkDataGridNode} b
3047 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
3049 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
3050 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
3051 return (aScore - bScore) || a._request.indentityCompare(b._request);
3055 * @param {string} propertyName
3056 * @param {boolean} revert
3057 * @param {!WebInspector.NetworkDataGridNode} a
3058 * @param {!WebInspector.NetworkDataGridNode} b
3061 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
3063 var aValue = a._request[propertyName];
3064 var bValue = b._request[propertyName];
3065 if (aValue > bValue)
3066 return revert ? -1 : 1;
3067 if (bValue > aValue)
3068 return revert ? 1 : -1;
3069 return a._request.indentityCompare(b._request);