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"), this._replayXHR.bind(this, request.requestId));
1205 contextMenu.appendSeparator();
1212 * @param {string} requestId
1214 _replayXHR: function(requestId)
1216 NetworkAgent.replayXHR(requestId);
1219 _harRequests: function()
1221 var requests = this._nodesByRequestId.values().map(function(node) { return node.request(); });
1222 var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1223 httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1224 return httpRequests.filter(WebInspector.NetworkLogView.NonDevToolsRequestsFilter);
1227 _copyAll: function()
1230 log: (new WebInspector.HARLog(this._harRequests())).build()
1232 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1236 * @param {!WebInspector.NetworkRequest} request
1238 _copyLocation: function(request)
1240 InspectorFrontendHost.copyText(request.url);
1244 * @param {!WebInspector.NetworkRequest} request
1246 _copyRequestHeaders: function(request)
1248 InspectorFrontendHost.copyText(request.requestHeadersText());
1252 * @param {!WebInspector.NetworkRequest} request
1254 _copyResponse: function(request)
1257 * @param {?string} content
1259 function callback(content)
1261 if (request.contentEncoded)
1262 content = request.asDataURL();
1263 InspectorFrontendHost.copyText(content || "");
1265 request.requestContent(callback);
1269 * @param {!WebInspector.NetworkRequest} request
1271 _copyResponseHeaders: function(request)
1273 InspectorFrontendHost.copyText(request.responseHeadersText);
1277 * @param {!WebInspector.NetworkRequest} request
1279 _copyCurlCommand: function(request)
1281 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1284 _exportAll: function()
1286 var filename = WebInspector.targetManager.inspectedPageDomain() + ".har";
1287 var stream = new WebInspector.FileOutputStream();
1288 stream.open(filename, openCallback.bind(this));
1291 * @param {boolean} accepted
1292 * @this {WebInspector.NetworkLogView}
1294 function openCallback(accepted)
1298 var progressIndicator = new WebInspector.ProgressIndicator();
1299 this._progressBarContainer.appendChild(progressIndicator.element);
1300 var harWriter = new WebInspector.HARWriter();
1301 harWriter.write(stream, this._harRequests(), progressIndicator);
1305 _clearBrowserCache: function()
1307 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1308 NetworkAgent.clearBrowserCache();
1311 _clearBrowserCookies: function()
1313 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1314 NetworkAgent.clearBrowserCookies();
1318 * @param {!WebInspector.NetworkRequest} request
1321 _matchRequest: function(request)
1323 var re = this._searchRegExp;
1326 return re.test(request.name()) || re.test(request.path());
1329 _clearSearchMatchedList: function()
1331 this._matchedRequestCount = -1;
1332 this._currentMatchedRequestNode = null;
1333 this._removeAllHighlights();
1336 _removeAllHighlights: function()
1338 this._removeAllNodeHighlights();
1339 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1340 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1341 this._highlightedSubstringChanges = [];
1346 * @param {boolean} reveal
1348 _highlightNthMatchedRequestForSearch: function(n, reveal)
1350 this._removeAllHighlights();
1352 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1353 var nodes = this._dataGrid.rootNode().children;
1356 for (var i = 0; i < nodes.length; ++i) {
1357 if (nodes[i]._isMatchingSearchQuery) {
1358 if (matchCount === n) {
1366 this._currentMatchedRequestNode = null;
1370 var request = node.request();
1371 var regExp = this._searchRegExp;
1372 var nameMatched = request.name().match(regExp);
1373 var pathMatched = request.path().match(regExp);
1374 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1375 this._toggleLargerRequests();
1377 WebInspector.Revealer.reveal(request);
1378 var highlightedSubstringChanges = node.highlightMatchedSubstring(regExp);
1379 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1381 this._currentMatchedRequestNode = node;
1382 this._currentMatchedRequestIndex = n;
1383 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
1387 * @param {string} query
1388 * @param {boolean} shouldJump
1389 * @param {boolean=} jumpBackwards
1391 performSearch: function(query, shouldJump, jumpBackwards)
1393 var currentMatchedRequestNode = this._currentMatchedRequestNode;
1394 this._clearSearchMatchedList();
1395 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1397 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1398 var nodes = this._dataGrid.rootNode().children;
1399 for (var i = 0; i < nodes.length; ++i)
1400 nodes[i]._isMatchingSearchQuery = this._matchRequest(nodes[i].request());
1401 var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
1402 if (!newMatchedRequestIndex && jumpBackwards)
1403 newMatchedRequestIndex = this._matchedRequestCount - 1;
1404 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
1408 * @param {?WebInspector.NetworkDataGridNode} node
1411 _updateMatchCountAndFindMatchIndex: function(node)
1413 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1414 var nodes = this._dataGrid.rootNode().children;
1417 for (var i = 0; i < nodes.length; ++i) {
1418 if (!nodes[i]._isMatchingSearchQuery)
1420 if (node === nodes[i])
1421 matchIndex = matchCount;
1424 if (this._matchedRequestCount !== matchCount) {
1425 this._matchedRequestCount = matchCount;
1426 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
1432 * @param {number} index
1435 _normalizeSearchResultIndex: function(index)
1437 return (index + this._matchedRequestCount) % this._matchedRequestCount;
1441 * @param {!WebInspector.NetworkDataGridNode} node
1444 _applyFilter: function(node)
1446 var request = node.request();
1447 if (!this._resourceTypeFilterUI.accept(request.type.name()))
1449 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1451 for (var i = 0; i < this._filters.length; ++i) {
1452 if (!this._filters[i](request))
1459 * @param {string} query
1461 _parseFilterQuery: function(query)
1463 var parsedQuery = this._suggestionBuilder.parseQuery(query);
1464 this._filters = parsedQuery.text.map(this._createTextFilter);
1465 for (var key in parsedQuery.filters) {
1466 var filterType = /** @type {!WebInspector.NetworkLogView.FilterType} */ (key);
1467 this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1472 * @param {string} text
1473 * @return {!WebInspector.NetworkLogView.Filter}
1475 _createTextFilter: function(text)
1477 var regexp = new RegExp(text.escapeForRegExp(), "i");
1478 return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1482 * @param {!WebInspector.NetworkLogView.FilterType} type
1483 * @param {string} value
1484 * @return {!WebInspector.NetworkLogView.Filter}
1486 _createFilter: function(type, value) {
1488 case WebInspector.NetworkLogView.FilterType.Domain:
1489 return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1491 case WebInspector.NetworkLogView.FilterType.HasResponseHeader:
1492 return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1494 case WebInspector.NetworkLogView.FilterType.Method:
1495 return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1497 case WebInspector.NetworkLogView.FilterType.MimeType:
1498 return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1500 case WebInspector.NetworkLogView.FilterType.Scheme:
1501 return WebInspector.NetworkLogView._requestSchemeFilter.bind(null, value);
1503 case WebInspector.NetworkLogView.FilterType.SetCookieDomain:
1504 return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1506 case WebInspector.NetworkLogView.FilterType.SetCookieName:
1507 return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1509 case WebInspector.NetworkLogView.FilterType.SetCookieValue:
1510 return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1512 case WebInspector.NetworkLogView.FilterType.StatusCode:
1513 return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1515 return this._createTextFilter(type + ":" + value);
1518 _filterRequests: function()
1520 this._removeAllHighlights();
1521 this._invalidateAllItems();
1525 jumpToPreviousSearchResult: function()
1527 if (!this._matchedRequestCount)
1529 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1530 this._highlightNthMatchedRequestForSearch(index, true);
1533 jumpToNextSearchResult: function()
1535 if (!this._matchedRequestCount)
1537 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1538 this._highlightNthMatchedRequestForSearch(index, true);
1541 searchCanceled: function()
1543 delete this._searchRegExp;
1544 this._clearSearchMatchedList();
1545 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1549 * @param {!WebInspector.NetworkRequest} request
1551 revealAndHighlightRequest: function(request)
1553 this._removeAllNodeHighlights();
1555 var node = this._nodesByRequestId.get(request.requestId);
1558 this._highlightNode(node);
1562 _removeAllNodeHighlights: function()
1564 if (this._highlightedNode) {
1565 this._highlightedNode.element().classList.remove("highlighted-row");
1566 delete this._highlightedNode;
1571 * @param {!WebInspector.NetworkDataGridNode} node
1573 _highlightNode: function(node)
1575 WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
1576 this._highlightedNode = node;
1580 * @param {!WebInspector.NetworkRequest} request
1583 _generateCurlCommand: function(request)
1585 var command = ["curl"];
1586 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1587 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1589 function escapeStringWin(str)
1591 /* Replace quote by double quote (but not by \") because it is
1592 recognized by both cmd.exe and MS Crt arguments parser.
1594 Replace % by "%" because it could be expanded to an environment
1595 variable value. So %% becomes "%""%". Even if an env variable ""
1596 (2 doublequotes) is declared, the cmd.exe will not
1597 substitute it with its value.
1599 Replace each backslash with double backslash to make sure
1600 MS Crt arguments parser won't collapse them.
1602 Replace new line outside of quotes since cmd.exe doesn't let
1605 return "\"" + str.replace(/"/g, "\"\"")
1606 .replace(/%/g, "\"%\"")
1607 .replace(/\\/g, "\\\\")
1608 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1611 function escapeStringPosix(str)
1613 function escapeCharacter(x)
1615 var code = x.charCodeAt(0);
1617 // Add leading zero when needed to not care about the next character.
1618 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1620 code = code.toString(16);
1621 return "\\u" + ("0000" + code).substr(code.length, 4);
1624 if (/[^\x20-\x7E]|\'/.test(str)) {
1625 // Use ANSI-C quoting syntax.
1626 return "$\'" + str.replace(/\\/g, "\\\\")
1627 .replace(/\'/g, "\\\'")
1628 .replace(/\n/g, "\\n")
1629 .replace(/\r/g, "\\r")
1630 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1632 // Use single quote syntax.
1633 return "'" + str + "'";
1637 // cURL command expected to run on the same platform that DevTools run
1638 // (it may be different from the inspected page platform).
1639 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1641 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1643 var inferredMethod = "GET";
1645 var requestContentType = request.requestContentType();
1646 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1647 data.push("--data");
1648 data.push(escapeString(request.requestFormData));
1649 ignoredHeaders["content-length"] = true;
1650 inferredMethod = "POST";
1651 } else if (request.requestFormData) {
1652 data.push("--data-binary");
1653 data.push(escapeString(request.requestFormData));
1654 ignoredHeaders["content-length"] = true;
1655 inferredMethod = "POST";
1658 if (request.requestMethod !== inferredMethod) {
1660 command.push(request.requestMethod);
1663 var requestHeaders = request.requestHeaders();
1664 for (var i = 0; i < requestHeaders.length; i++) {
1665 var header = requestHeaders[i];
1666 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1667 if (name.toLowerCase() in ignoredHeaders)
1670 command.push(escapeString(name + ": " + header.value));
1672 command = command.concat(data);
1673 command.push("--compressed");
1674 return command.join(" ");
1677 __proto__: WebInspector.VBox.prototype
1680 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1681 WebInspector.NetworkLogView.Filter;
1684 * @param {!RegExp} regex
1685 * @param {!WebInspector.NetworkRequest} request
1688 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1690 return regex.test(request.name()) || regex.test(request.path());
1694 * @param {string} value
1695 * @param {!WebInspector.NetworkRequest} request
1698 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1700 return request.domain === value;
1704 * @param {string} value
1705 * @param {!WebInspector.NetworkRequest} request
1708 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1710 return request.responseHeaderValue(value) !== undefined;
1714 * @param {string} value
1715 * @param {!WebInspector.NetworkRequest} request
1718 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1720 return request.requestMethod === value;
1724 * @param {string} value
1725 * @param {!WebInspector.NetworkRequest} request
1728 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1730 return request.mimeType === value;
1734 * @param {string} value
1735 * @param {!WebInspector.NetworkRequest} request
1738 WebInspector.NetworkLogView._requestSchemeFilter = function(value, request)
1740 return request.scheme === value;
1744 * @param {string} value
1745 * @param {!WebInspector.NetworkRequest} request
1748 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1750 var cookies = request.responseCookies;
1751 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1752 if (cookies[i].domain() === value)
1759 * @param {string} value
1760 * @param {!WebInspector.NetworkRequest} request
1763 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1765 var cookies = request.responseCookies;
1766 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1767 if (cookies[i].name() === value)
1774 * @param {string} value
1775 * @param {!WebInspector.NetworkRequest} request
1778 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1780 var cookies = request.responseCookies;
1781 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1782 if (cookies[i].value() === value)
1789 * @param {string} value
1790 * @param {!WebInspector.NetworkRequest} request
1793 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1795 return ("" + request.statusCode) === value;
1799 * @param {!WebInspector.NetworkRequest} request
1802 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1804 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1808 * @param {!WebInspector.NetworkRequest} request
1811 WebInspector.NetworkLogView.NonDevToolsRequestsFilter = function(request)
1813 return !WebInspector.NetworkManager.hasDevToolsRequestHeader(request);
1817 * @param {!WebInspector.NetworkRequest} request
1820 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1822 return request.finished;
1825 WebInspector.NetworkLogView.EventTypes = {
1826 ViewCleared: "ViewCleared",
1827 RowSizeChanged: "RowSizeChanged",
1828 RequestSelected: "RequestSelected",
1829 SearchCountUpdated: "SearchCountUpdated",
1830 SearchIndexUpdated: "SearchIndexUpdated"
1835 * @implements {WebInspector.ContextMenu.Provider}
1836 * @implements {WebInspector.Searchable}
1837 * @extends {WebInspector.Panel}
1839 WebInspector.NetworkPanel = function()
1841 WebInspector.Panel.call(this, "network");
1842 this.registerRequiredCSS("networkPanel.css");
1844 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1845 this._filterBar = new WebInspector.FilterBar();
1846 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1847 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1848 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1849 this._filterBar.setName("networkPanel");
1851 this._searchableView = new WebInspector.SearchableView(this);
1852 this._searchableView.show(this.element);
1853 var contentsElement = this._searchableView.element;
1855 this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1856 this._splitView.show(contentsElement);
1857 this._splitView.hideMain();
1859 var defaultColumnsVisibility = WebInspector.NetworkLogView.defaultColumnsVisibility;
1860 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1861 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1862 var columnsVisibility = {};
1863 for (var columnId in defaultColumnsVisibility)
1864 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1865 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1867 /** @type {!WebInspector.NetworkLogView} */
1868 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1869 this._networkLogView.show(this._splitView.sidebarElement());
1871 var viewsContainerView = new WebInspector.VBox();
1872 this._viewsContainerElement = viewsContainerView.element;
1873 this._viewsContainerElement.id = "network-views";
1874 if (!this._networkLogView.usesLargeRows())
1875 this._viewsContainerElement.classList.add("small");
1876 viewsContainerView.show(this._splitView.mainElement());
1878 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1879 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1880 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1881 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1882 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1884 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1885 this._closeButtonElement.id = "network-close-button";
1886 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1887 this._viewsContainerElement.appendChild(this._closeButtonElement);
1889 var statusBarItems = this._networkLogView.statusBarItems();
1890 for (var i = 0; i < statusBarItems.length; ++i)
1891 this._panelStatusBarElement.appendChild(statusBarItems[i]);
1894 * @this {WebInspector.NetworkPanel}
1895 * @return {?WebInspector.SourceFrame}
1897 function sourceFrameGetter()
1899 return this._networkItemView.currentSourceFrame();
1901 WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1904 WebInspector.NetworkPanel.prototype = {
1906 * @param {!WebInspector.Event} event
1908 _onFiltersToggled: function(event)
1910 var toggled = /** @type {boolean} */ (event.data);
1911 this._filtersContainer.classList.toggle("hidden", !toggled);
1912 this.element.classList.toggle("filters-toggled", toggled);
1917 * @return {!Array.<!Element>}
1919 elementsToRestoreScrollPositionsFor: function()
1921 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1925 * @return {!WebInspector.SearchableView}
1927 searchableView: function()
1929 return this._searchableView;
1933 * @param {!KeyboardEvent} event
1935 handleShortcut: function(event)
1937 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1938 this._toggleGridMode();
1939 event.handled = true;
1943 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1946 wasShown: function()
1948 WebInspector.Panel.prototype.wasShown.call(this);
1952 * @param {!WebInspector.NetworkRequest} request
1954 revealAndHighlightRequest: function(request)
1956 this._toggleGridMode();
1958 this._networkLogView.revealAndHighlightRequest(request);
1962 * @param {!WebInspector.Event} event
1964 _onViewCleared: function(event)
1966 this._closeVisibleRequest();
1967 this._toggleGridMode();
1968 this._viewsContainerElement.removeChildren();
1969 this._viewsContainerElement.appendChild(this._closeButtonElement);
1973 * @param {!WebInspector.Event} event
1975 _onRowSizeChanged: function(event)
1977 this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1981 * @param {!WebInspector.Event} event
1983 _onSearchCountUpdated: function(event)
1985 var count = /** @type {number} */ (event.data);
1986 this._searchableView.updateSearchMatchesCount(count);
1990 * @param {!WebInspector.Event} event
1992 _onSearchIndexUpdated: function(event)
1994 var index = /** @type {number} */ (event.data);
1995 this._searchableView.updateCurrentMatchIndex(index);
1999 * @param {!WebInspector.Event} event
2001 _onRequestSelected: function(event)
2003 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
2004 this._showRequest(request);
2008 * @param {?WebInspector.NetworkRequest} request
2010 _showRequest: function(request)
2015 this._toggleViewingRequestMode();
2017 if (this._networkItemView) {
2018 this._networkItemView.detach();
2019 delete this._networkItemView;
2022 var view = new WebInspector.NetworkItemView(request);
2023 view.show(this._viewsContainerElement);
2024 this._networkItemView = view;
2027 _closeVisibleRequest: function()
2029 this.element.classList.remove("viewing-resource");
2031 if (this._networkItemView) {
2032 this._networkItemView.detach();
2033 delete this._networkItemView;
2037 _toggleGridMode: function()
2039 if (this._viewingRequestMode) {
2040 this._viewingRequestMode = false;
2041 this.element.classList.remove("viewing-resource");
2042 this._splitView.hideMain();
2045 this._networkLogView.switchViewMode(true);
2046 this._networkLogView.setAllowPopover(true);
2047 this._networkLogView._allowRequestSelection = false;
2050 _toggleViewingRequestMode: function()
2052 if (this._viewingRequestMode)
2054 this._viewingRequestMode = true;
2056 this.element.classList.add("viewing-resource");
2057 this._splitView.showBoth();
2058 this._networkLogView.setAllowPopover(false);
2059 this._networkLogView._allowRequestSelection = true;
2060 this._networkLogView.switchViewMode(false);
2064 * @param {string} query
2065 * @param {boolean} shouldJump
2066 * @param {boolean=} jumpBackwards
2068 performSearch: function(query, shouldJump, jumpBackwards)
2070 this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
2073 jumpToPreviousSearchResult: function()
2075 this._networkLogView.jumpToPreviousSearchResult();
2078 jumpToNextSearchResult: function()
2080 this._networkLogView.jumpToNextSearchResult();
2083 searchCanceled: function()
2085 this._networkLogView.searchCanceled();
2089 * @param {!Event} event
2090 * @param {!WebInspector.ContextMenu} contextMenu
2091 * @param {!Object} target
2092 * @this {WebInspector.NetworkPanel}
2094 appendApplicableItems: function(event, contextMenu, target)
2097 * @this {WebInspector.NetworkPanel}
2099 function reveal(request)
2101 WebInspector.inspectorView.setCurrentPanel(this);
2102 this.revealAndHighlightRequest(request);
2106 * @this {WebInspector.NetworkPanel}
2108 function appendRevealItem(request)
2110 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
2111 contextMenu.appendItem(revealText, reveal.bind(this, request));
2114 if (target instanceof WebInspector.Resource) {
2115 var resource = /** @type {!WebInspector.Resource} */ (target);
2116 if (resource.request)
2117 appendRevealItem.call(this, resource.request);
2120 if (target instanceof WebInspector.UISourceCode) {
2121 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
2122 var resource = WebInspector.resourceForURL(uiSourceCode.url);
2123 if (resource && resource.request)
2124 appendRevealItem.call(this, resource.request);
2128 if (!(target instanceof WebInspector.NetworkRequest))
2130 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2131 if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2134 appendRevealItem.call(this, request);
2137 __proto__: WebInspector.Panel.prototype
2142 * @implements {WebInspector.ContextMenu.Provider}
2144 WebInspector.NetworkPanel.ContextMenuProvider = function()
2148 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2150 * @param {!Event} event
2151 * @param {!WebInspector.ContextMenu} contextMenu
2152 * @param {!Object} target
2154 appendApplicableItems: function(event, contextMenu, target)
2156 WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2162 * @implements {WebInspector.Revealer}
2164 WebInspector.NetworkPanel.RequestRevealer = function()
2168 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2170 * @param {!Object} request
2171 * @param {number=} lineNumber
2173 reveal: function(request, lineNumber)
2175 if (request instanceof WebInspector.NetworkRequest) {
2176 var panel = /** @type {?WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network"));
2178 panel.revealAndHighlightRequest(request);
2185 * @implements {WebInspector.TimelineGrid.Calculator}
2187 WebInspector.NetworkTimeCalculator = function(startAtZero)
2189 this.startAtZero = startAtZero;
2192 /** @type {!WebInspector.UIStringFormat} */
2193 WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat = new WebInspector.UIStringFormat("%s latency, %s download (%s total)");
2195 /** @type {!WebInspector.UIStringFormat} */
2196 WebInspector.NetworkTimeCalculator._latencyFormat = new WebInspector.UIStringFormat("%s latency");
2198 /** @type {!WebInspector.UIStringFormat} */
2199 WebInspector.NetworkTimeCalculator._downloadFormat = new WebInspector.UIStringFormat("%s download");
2201 /** @type {!WebInspector.UIStringFormat} */
2202 WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat = new WebInspector.UIStringFormat("%s (from ServiceWorker)");
2204 /** @type {!WebInspector.UIStringFormat} */
2205 WebInspector.NetworkTimeCalculator._fromCacheFormat = new WebInspector.UIStringFormat("%s (from cache)");
2207 WebInspector.NetworkTimeCalculator.prototype = {
2212 paddingLeft: function()
2219 * @param {number} time
2222 computePosition: function(time)
2224 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2229 * @param {number} value
2230 * @param {number=} precision
2233 formatTime: function(value, precision)
2235 return Number.secondsToString(value);
2242 minimumBoundary: function()
2244 return this._minimumBoundary;
2251 zeroTime: function()
2253 return this._minimumBoundary;
2260 maximumBoundary: function()
2262 return this._maximumBoundary;
2269 boundarySpan: function()
2271 return this._maximumBoundary - this._minimumBoundary;
2276 delete this._minimumBoundary;
2277 delete this._maximumBoundary;
2283 _value: function(item)
2289 * @param {number} clientWidth
2291 setDisplayWindow: function(clientWidth)
2293 this._workingArea = clientWidth;
2297 * @param {!WebInspector.NetworkRequest} request
2298 * @return {!{start: number, middle: number, end: number}}
2300 computeBarGraphPercentages: function(request)
2302 if (request.startTime !== -1)
2303 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2307 if (request.responseReceivedTime !== -1)
2308 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2310 var middle = (this.startAtZero ? start : 100);
2312 if (request.endTime !== -1)
2313 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2315 var end = (this.startAtZero ? middle : 100);
2317 if (this.startAtZero) {
2323 return {start: start, middle: middle, end: end};
2327 * @param {number} eventTime
2330 computePercentageFromEventTime: function(eventTime)
2332 // This function computes a percentage in terms of the total loading time
2333 // of a specific event. If startAtZero is set, then this is useless, and we
2334 // want to return 0.
2335 if (eventTime !== -1 && !this.startAtZero)
2336 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2342 * @param {number} eventTime
2345 updateBoundariesForEventTime: function(eventTime)
2347 if (eventTime === -1 || this.startAtZero)
2350 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2351 this._maximumBoundary = eventTime;
2358 * @param {!WebInspector.NetworkRequest} request
2359 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2361 computeBarGraphLabels: function(request)
2363 var rightLabel = "";
2364 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2365 rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2367 var hasLatency = request.latency > 0;
2369 var leftLabel = Number.secondsToString(request.latency);
2371 var leftLabel = rightLabel;
2374 return {left: leftLabel, right: rightLabel};
2376 if (hasLatency && rightLabel) {
2377 var total = Number.secondsToString(request.duration);
2378 var tooltip = WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat.format(leftLabel, rightLabel, total);
2379 } else if (hasLatency)
2380 var tooltip = WebInspector.NetworkTimeCalculator._latencyFormat.format(leftLabel);
2381 else if (rightLabel)
2382 var tooltip = WebInspector.NetworkTimeCalculator._downloadFormat.format(rightLabel);
2384 if (request.fetchedViaServiceWorker)
2385 tooltip = WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat.format(tooltip);
2386 else if (request.cached)
2387 tooltip = WebInspector.NetworkTimeCalculator._fromCacheFormat.format(tooltip);
2388 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2392 * @param {!WebInspector.NetworkRequest} request
2395 updateBoundaries: function(request)
2397 var didChange = false;
2400 if (this.startAtZero)
2403 lowerBound = this._lowerBound(request);
2405 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2406 this._minimumBoundary = lowerBound;
2410 var upperBound = this._upperBound(request);
2411 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2412 this._maximumBoundary = upperBound;
2420 * @param {!WebInspector.NetworkRequest} request
2423 _lowerBound: function(request)
2429 * @param {!WebInspector.NetworkRequest} request
2432 _upperBound: function(request)
2440 * @extends {WebInspector.NetworkTimeCalculator}
2442 WebInspector.NetworkTransferTimeCalculator = function()
2444 WebInspector.NetworkTimeCalculator.call(this, false);
2447 WebInspector.NetworkTransferTimeCalculator.prototype = {
2450 * @param {number} value
2451 * @param {number=} precision
2454 formatTime: function(value, precision)
2456 return Number.secondsToString(value - this.zeroTime());
2461 * @param {!WebInspector.NetworkRequest} request
2464 _lowerBound: function(request)
2466 return request.startTime;
2471 * @param {!WebInspector.NetworkRequest} request
2474 _upperBound: function(request)
2476 return request.endTime;
2479 __proto__: WebInspector.NetworkTimeCalculator.prototype
2484 * @extends {WebInspector.NetworkTimeCalculator}
2486 WebInspector.NetworkTransferDurationCalculator = function()
2488 WebInspector.NetworkTimeCalculator.call(this, true);
2491 WebInspector.NetworkTransferDurationCalculator.prototype = {
2494 * @param {number} value
2495 * @param {number=} precision
2498 formatTime: function(value, precision)
2500 return Number.secondsToString(value);
2505 * @param {!WebInspector.NetworkRequest} request
2508 _upperBound: function(request)
2510 return request.duration;
2513 __proto__: WebInspector.NetworkTimeCalculator.prototype
2518 * @extends {WebInspector.SortableDataGridNode}
2519 * @param {!WebInspector.NetworkLogView} parentView
2520 * @param {!WebInspector.NetworkRequest} request
2522 WebInspector.NetworkDataGridNode = function(parentView, request)
2524 WebInspector.SortableDataGridNode.call(this, {});
2525 this._parentView = parentView;
2526 this._request = request;
2527 this._linkifier = new WebInspector.Linkifier();
2528 this._isFilteredOut = true;
2529 this._isMatchingSearchQuery = false;
2530 this._staleGraph = true;
2533 WebInspector.NetworkDataGridNode.prototype = {
2535 * @return {!WebInspector.NetworkRequest}
2539 return this._request;
2546 nodeSelfHeight: function() {
2547 return this._parentView.rowHeight();
2551 createCells: function()
2553 this._nameCell = null;
2554 this._timelineCell = null;
2556 this._element.classList.toggle("network-error-row", this._isFailed());
2557 WebInspector.SortableDataGridNode.prototype.createCells.call(this);
2559 this._updateGraph();
2564 * @param {string} columnIdentifier
2565 * @return {!Element}
2567 createCell: function(columnIdentifier)
2569 var cell = this.createTD(columnIdentifier);
2570 switch (columnIdentifier) {
2571 case "name": this._renderNameCell(cell); break;
2572 case "timeline": this._createTimelineBar(cell); break;
2573 case "method": cell.setTextAndTitle(this._request.requestMethod); break;
2574 case "status": this._renderStatusCell(cell); break;
2575 case "scheme": cell.setTextAndTitle(this._request.scheme); break;
2576 case "domain": cell.setTextAndTitle(this._request.domain); break;
2577 case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
2578 case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
2579 case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
2580 case "connectionId": cell.setTextAndTitle(this._request.connectionId); break;
2581 case "type": cell.setTextAndTitle(this._request.mimeType || this._request.requestContentType() || ""); break;
2582 case "initiator": this._renderInitiatorCell(cell); break;
2583 case "size": this._renderSizeCell(cell); break;
2584 case "time": this._renderTimeCell(cell); break;
2585 default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
2592 * @param {?Array} array
2595 _arrayLength: function(array)
2597 return array ? "" + array.length : "";
2604 willAttach: function()
2606 if (this._staleGraph)
2607 this._updateGraph();
2610 wasDetached: function()
2612 if (this._linkifiedInitiatorAnchor)
2613 this._linkifiedInitiatorAnchor.remove();
2618 this._linkifier.reset();
2621 _onClick: function()
2623 if (!this._parentView.allowRequestSelection())
2629 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2630 WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
2632 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2633 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2634 url: this._request.url
2639 * @param {!RegExp=} regexp
2640 * @return {!Array.<!Object>}
2642 highlightMatchedSubstring: function(regexp)
2644 // Ensure element is created.
2646 var domChanges = [];
2647 var matchInfo = this._nameCell.textContent.match(regexp);
2649 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2653 _openInNewTab: function()
2655 InspectorFrontendHost.openInNewTab(this._request.url);
2660 return this._parentView.allowRequestSelection();
2664 * @param {!Element} cell
2666 _createTimelineBar: function(cell)
2668 cell = cell.createChild("div");
2669 this._timelineCell = cell;
2671 cell.className = "network-graph-side";
2673 this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
2674 this._barAreaElement.request = this._request;
2676 var type = this._request.type.name();
2677 var cached = this._request.cached;
2679 this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
2680 this._barLeftElement.classList.add(type, "waiting");
2681 this._barLeftElement.classList.toggle("cached", cached);
2683 this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
2684 this._barRightElement.classList.add(type);
2685 this._barRightElement.classList.toggle("cached", cached);
2687 this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
2688 this._labelLeftElement.classList.add("waiting");
2690 this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
2692 cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2698 _isFailed: function()
2700 return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
2704 * @param {!Element} cell
2706 _renderNameCell: function(cell)
2708 this._nameCell = cell;
2709 cell.addEventListener("click", this._onClick.bind(this), false);
2710 cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2712 if (this._request.type === WebInspector.resourceTypes.Image) {
2713 var previewImage = document.createElementWithClass("img", "image-network-icon-preview");
2714 this._request.populateImageSource(previewImage);
2716 iconElement = document.createElementWithClass("div", "icon");
2717 iconElement.appendChild(previewImage);
2719 iconElement = document.createElementWithClass("img", "icon");
2721 iconElement.classList.add(this._request.type.name());
2723 cell.appendChild(iconElement);
2724 cell.createTextChild(this._request.name());
2725 this._appendSubtitle(cell, this._request.path());
2726 cell.title = this._request.url;
2730 * @param {!Element} cell
2732 _renderStatusCell: function(cell)
2734 cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2736 if (this._request.failed && !this._request.canceled) {
2737 var failText = WebInspector.UIString("(failed)");
2738 if (this._request.localizedFailDescription) {
2739 cell.createTextChild(failText);
2740 this._appendSubtitle(cell, this._request.localizedFailDescription);
2741 cell.title = failText + " " + this._request.localizedFailDescription;
2743 cell.setTextAndTitle(failText);
2744 } else if (this._request.statusCode) {
2745 cell.createTextChild("" + this._request.statusCode);
2746 this._appendSubtitle(cell, this._request.statusText);
2747 cell.title = this._request.statusCode + " " + this._request.statusText;
2748 } else if (this._request.parsedURL.isDataURL()) {
2749 cell.setTextAndTitle(WebInspector.UIString("(data)"));
2750 } else if (this._request.canceled) {
2751 cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2752 } else if (this._request.finished) {
2753 cell.setTextAndTitle(WebInspector.UIString("Finished"));
2755 cell.setTextAndTitle(WebInspector.UIString("(pending)"));
2760 * @param {!Element} cell
2762 _renderInitiatorCell: function(cell)
2764 var request = this._request;
2765 var initiator = request.initiatorInfo();
2767 switch (initiator.type) {
2768 case WebInspector.NetworkRequest.InitiatorType.Parser:
2769 cell.title = initiator.url + ":" + initiator.lineNumber;
2770 cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2771 this._appendSubtitle(cell, WebInspector.UIString("Parser"));
2774 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2775 cell.title = initiator.url;
2776 console.assert(request.redirectSource);
2777 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2778 cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2779 this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
2782 case WebInspector.NetworkRequest.InitiatorType.Script:
2783 if (!this._linkifiedInitiatorAnchor) {
2784 this._linkifiedInitiatorAnchor = this._linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2785 this._linkifiedInitiatorAnchor.title = "";
2787 cell.appendChild(this._linkifiedInitiatorAnchor);
2788 this._appendSubtitle(cell, WebInspector.UIString("Script"));
2789 cell.classList.add("network-script-initiated");
2790 cell.request = request;
2795 cell.classList.add("network-dim-cell");
2796 cell.setTextAndTitle(WebInspector.UIString("Other"));
2801 * @param {!Element} cell
2803 _renderSizeCell: function(cell)
2805 if (this._request.fetchedViaServiceWorker) {
2806 cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)"));
2807 cell.classList.add("network-dim-cell");
2808 } else if (this._request.cached) {
2809 cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2810 cell.classList.add("network-dim-cell");
2812 var resourceSize = Number.bytesToString(this._request.resourceSize);
2813 var transferSize = Number.bytesToString(this._request.transferSize);
2814 cell.setTextAndTitle(transferSize);
2815 this._appendSubtitle(cell, resourceSize);
2820 * @param {!Element} cell
2822 _renderTimeCell: function(cell)
2824 if (this._request.duration > 0) {
2825 cell.setTextAndTitle(Number.secondsToString(this._request.duration));
2826 this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
2828 cell.classList.add("network-dim-cell");
2829 cell.setTextAndTitle(WebInspector.UIString("Pending"));
2834 * @param {!Element} cellElement
2835 * @param {string} subtitleText
2837 _appendSubtitle: function(cellElement, subtitleText)
2839 var subtitleElement = document.createElement("div");
2840 subtitleElement.className = "network-cell-subtitle";
2841 subtitleElement.textContent = subtitleText;
2842 cellElement.appendChild(subtitleElement);
2845 refreshGraph: function()
2847 if (!this._timelineCell)
2849 this._staleGraph = true;
2850 if (this.attached())
2851 this.dataGrid.scheduleUpdate();
2854 _updateGraph: function()
2856 this._staleGraph = false;
2857 if (!this._timelineCell)
2860 var calculator = this._parentView.calculator();
2861 var percentages = calculator.computeBarGraphPercentages(this._request);
2862 this._percentages = percentages;
2864 this._barAreaElement.classList.remove("hidden");
2866 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2867 this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
2869 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2870 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2872 var labels = calculator.computeBarGraphLabels(this._request);
2873 this._labelLeftElement.textContent = labels.left;
2874 this._labelRightElement.textContent = labels.right;
2876 var tooltip = (labels.tooltip || "");
2877 this._barLeftElement.title = tooltip;
2878 this._labelLeftElement.title = tooltip;
2879 this._labelRightElement.title = tooltip;
2880 this._barRightElement.title = tooltip;
2883 _refreshLabelPositions: function()
2885 if (!this._percentages)
2887 this._labelLeftElement.style.removeProperty("left");
2888 this._labelLeftElement.style.removeProperty("right");
2889 this._labelLeftElement.classList.remove("before");
2890 this._labelLeftElement.classList.remove("hidden");
2892 this._labelRightElement.style.removeProperty("left");
2893 this._labelRightElement.style.removeProperty("right");
2894 this._labelRightElement.classList.remove("after");
2895 this._labelRightElement.classList.remove("hidden");
2897 const labelPadding = 10;
2898 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2899 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2901 if (this._barLeftElement) {
2902 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2903 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2905 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2906 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2909 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2910 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2912 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2913 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2914 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2916 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2917 var leftHidden = true;
2919 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2920 var rightHidden = true;
2922 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2923 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2924 if (labelBefore && !labelAfter)
2926 else if (labelAfter && !labelBefore)
2932 this._labelLeftElement.classList.add("hidden");
2933 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2934 this._labelLeftElement.classList.add("before");
2936 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2937 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2942 this._labelRightElement.classList.add("hidden");
2943 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2944 this._labelRightElement.classList.add("after");
2946 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2947 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2951 __proto__: WebInspector.SortableDataGridNode.prototype
2955 * @param {!WebInspector.NetworkDataGridNode} a
2956 * @param {!WebInspector.NetworkDataGridNode} b
2959 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2961 var aFileName = a._request.name();
2962 var bFileName = b._request.name();
2963 if (aFileName > bFileName)
2965 if (bFileName > aFileName)
2967 return a._request.indentityCompare(b._request);
2971 * @param {!WebInspector.NetworkDataGridNode} a
2972 * @param {!WebInspector.NetworkDataGridNode} b
2975 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2977 var aRemoteAddress = a._request.remoteAddress();
2978 var bRemoteAddress = b._request.remoteAddress();
2979 if (aRemoteAddress > bRemoteAddress)
2981 if (bRemoteAddress > aRemoteAddress)
2983 return a._request.indentityCompare(b._request);
2987 * @param {!WebInspector.NetworkDataGridNode} a
2988 * @param {!WebInspector.NetworkDataGridNode} b
2991 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2993 if (b._request.cached && !a._request.cached)
2995 if (a._request.cached && !b._request.cached)
2997 return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
3001 * @param {!WebInspector.NetworkDataGridNode} a
3002 * @param {!WebInspector.NetworkDataGridNode} b
3005 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
3007 var aInitiator = a._request.initiatorInfo();
3008 var bInitiator = b._request.initiatorInfo();
3010 if (aInitiator.type < bInitiator.type)
3012 if (aInitiator.type > bInitiator.type)
3015 if (typeof aInitiator.__source === "undefined")
3016 aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
3017 if (typeof bInitiator.__source === "undefined")
3018 bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
3020 if (aInitiator.__source < bInitiator.__source)
3022 if (aInitiator.__source > bInitiator.__source)
3025 if (aInitiator.lineNumber < bInitiator.lineNumber)
3027 if (aInitiator.lineNumber > bInitiator.lineNumber)
3030 if (aInitiator.columnNumber < bInitiator.columnNumber)
3032 if (aInitiator.columnNumber > bInitiator.columnNumber)
3035 return a._request.indentityCompare(b._request);
3039 * @param {!WebInspector.NetworkDataGridNode} a
3040 * @param {!WebInspector.NetworkDataGridNode} b
3043 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
3045 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
3046 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
3047 return (aScore - bScore) || a._request.indentityCompare(b._request);
3051 * @param {!WebInspector.NetworkDataGridNode} a
3052 * @param {!WebInspector.NetworkDataGridNode} b
3055 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
3057 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
3058 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
3059 return (aScore - bScore) || a._request.indentityCompare(b._request);
3063 * @param {string} propertyName
3064 * @param {boolean} revert
3065 * @param {!WebInspector.NetworkDataGridNode} a
3066 * @param {!WebInspector.NetworkDataGridNode} b
3069 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
3071 var aValue = a._request[propertyName];
3072 var bValue = b._request[propertyName];
3073 if (aValue > bValue)
3074 return revert ? -1 : 1;
3075 if (bValue > aValue)
3076 return revert ? 1 : -1;
3077 return a._request.indentityCompare(b._request);