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 = {};
52 this._mainRequestLoadTime = -1;
53 this._mainRequestDOMContentLoadedTime = -1;
54 this._matchedRequestCount = 0;
55 this._highlightedSubstringChanges = [];
57 /** @type {!Array.<!WebInspector.NetworkLogView.Filter>} */
60 this._currentMatchedRequestNode = null;
61 this._currentMatchedRequestIndex = -1;
63 this._createStatusbarButtons();
64 this._createStatusBarItems();
65 this._linkifier = new WebInspector.Linkifier();
67 this._allowPopover = true;
73 this._resetSuggestionBuilder();
74 this._initializeView();
75 this._recordButton.toggled = true;
77 WebInspector.targetManager.observeTargets(this);
78 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
79 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
80 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
82 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
83 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
84 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
85 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
88 WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
89 WebInspector.NetworkLogView._responseHeaderColumns = ["Cache-Control", "Connection", "Content-Encoding", "Content-Length", "ETag", "Keep-Alive", "Last-Modified", "Server", "Vary"];
90 WebInspector.NetworkLogView._defaultColumnsVisibility = {
91 method: true, status: true, scheme: false, domain: false, remoteAddress: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true,
92 "Cache-Control": false, "Connection": false, "Content-Encoding": false, "Content-Length": false, "ETag": false, "Keep-Alive": false, "Last-Modified": false, "Server": false, "Vary": false
94 WebInspector.NetworkLogView._defaultRefreshDelay = 500;
96 /** @type {!Object.<string, string>} */
97 WebInspector.NetworkLogView._columnTitles = {
98 "name": WebInspector.UIString("Name"),
99 "method": WebInspector.UIString("Method"),
100 "status": WebInspector.UIString("Status"),
101 "scheme": WebInspector.UIString("Scheme"),
102 "domain": WebInspector.UIString("Domain"),
103 "remoteAddress": WebInspector.UIString("Remote Address"),
104 "type": WebInspector.UIString("Type"),
105 "initiator": WebInspector.UIString("Initiator"),
106 "cookies": WebInspector.UIString("Cookies"),
107 "setCookies": WebInspector.UIString("Set-Cookies"),
108 "size": WebInspector.UIString("Size"),
109 "time": WebInspector.UIString("Time"),
110 "timeline": WebInspector.UIString("Timeline"),
112 // Response header columns
113 "Cache-Control": WebInspector.UIString("Cache-Control"),
114 "Connection": WebInspector.UIString("Connection"),
115 "Content-Encoding": WebInspector.UIString("Content-Encoding"),
116 "Content-Length": WebInspector.UIString("Content-Length"),
117 "ETag": WebInspector.UIString("ETag"),
118 "Keep-Alive": WebInspector.UIString("Keep-Alive"),
119 "Last-Modified": WebInspector.UIString("Last-Modified"),
120 "Server": WebInspector.UIString("Server"),
121 "Vary": WebInspector.UIString("Vary")
124 WebInspector.NetworkLogView.prototype = {
126 * @param {!WebInspector.Target} target
128 targetAdded: function(target)
130 target.networkLog.requests.forEach(this._appendRequest.bind(this));
134 * @param {!WebInspector.Target} target
136 targetRemoved: function(target)
140 _addFilters: function()
142 this._textFilterUI = new WebInspector.TextFilterUI();
143 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
144 this._filterBar.addFilter(this._textFilterUI);
147 for (var typeId in WebInspector.resourceTypes) {
148 var resourceType = WebInspector.resourceTypes[typeId];
149 types.push({name: resourceType.name(), label: resourceType.categoryTitle()});
151 this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI(types, WebInspector.settings.networkResourceTypeFilters);
152 this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
153 this._filterBar.addFilter(this._resourceTypeFilterUI);
155 var dataURLSetting = WebInspector.settings.networkHideDataURL;
156 this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
157 this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
158 this._filterBar.addFilter(this._dataURLFilterUI);
161 _resetSuggestionBuilder: function()
163 this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkPanel._searchKeys);
164 this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
168 * @param {!WebInspector.Event} event
170 _filterChanged: function(event)
172 this._removeAllNodeHighlights();
173 this._parseFilterQuery(this._textFilterUI.value());
174 this._filterRequests();
177 _initializeView: function()
179 this.element.id = "network-container";
181 this._createSortingFunctions();
183 this._createTimelineGrid();
184 this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
186 this._updateRowsSize();
188 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
189 // Enable faster hint.
190 this._popoverHelper.setTimeout(100);
192 this._setCalculator(new WebInspector.NetworkTransferTimeCalculator());
194 this.switchViewMode(true);
198 * @return {!Array.<!Element>}
200 statusBarItems: function()
203 this._recordButton.element,
204 this._clearButton.element,
205 this._filterBar.filterButton().element,
206 this._largerRequestsButton.element,
207 this._preserveLogCheckbox.element,
208 this._disableCacheCheckbox.element,
209 this._progressBarContainer];
215 usesLargeRows: function()
217 return !!WebInspector.settings.resourcesLargeRows.get();
221 * @param {boolean} flag
223 setAllowPopover: function(flag)
225 this._allowPopover = flag;
229 * @return {!Array.<!Element>}
231 elementsToRestoreScrollPositionsFor: function()
233 if (!this._dataGrid) // Not initialized yet.
235 return [this._dataGrid.scrollContainer];
238 _createTimelineGrid: function()
240 this._timelineGrid = new WebInspector.TimelineGrid();
241 this._timelineGrid.element.classList.add("network-timeline-grid");
242 this._dataGrid.element.appendChild(this._timelineGrid.element);
245 _createTable: function()
250 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
251 title: WebInspector.NetworkLogView._columnTitles["name"],
259 title: WebInspector.NetworkLogView._columnTitles["method"],
266 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
267 title: WebInspector.NetworkLogView._columnTitles["status"],
274 title: WebInspector.NetworkLogView._columnTitles["scheme"],
281 title: WebInspector.NetworkLogView._columnTitles["domain"],
288 title: WebInspector.NetworkLogView._columnTitles["remoteAddress"],
291 align: WebInspector.DataGrid.Align.Right
296 title: WebInspector.NetworkLogView._columnTitles["type"],
303 title: WebInspector.NetworkLogView._columnTitles["initiator"],
310 title: WebInspector.NetworkLogView._columnTitles["cookies"],
313 align: WebInspector.DataGrid.Align.Right
318 title: WebInspector.NetworkLogView._columnTitles["setCookies"],
321 align: WebInspector.DataGrid.Align.Right
326 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
327 title: WebInspector.NetworkLogView._columnTitles["size"],
330 align: WebInspector.DataGrid.Align.Right
335 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
336 title: WebInspector.NetworkLogView._columnTitles["time"],
339 align: WebInspector.DataGrid.Align.Right
342 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
343 for (var i = 0; i < responseHeaderColumns.length; ++i) {
344 var headerName = responseHeaderColumns[i];
347 title: WebInspector.NetworkLogView._columnTitles[headerName],
350 if (headerName === "Content-Length")
351 descriptor.align = WebInspector.DataGrid.Align.Right;
352 columns.push(descriptor);
357 titleDOMFragment: document.createDocumentFragment(),
358 title: WebInspector.NetworkLogView._columnTitles["timeline"],
361 sort: WebInspector.DataGrid.Order.Ascending
364 this._dataGrid = new WebInspector.SortableDataGrid(columns);
365 this._dataGrid.setStickToBottom(true);
366 this._updateColumns();
367 this._dataGrid.setName("networkLog");
368 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
369 this._dataGrid.element.classList.add("network-log-grid");
370 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
371 this._dataGrid.show(this.element);
373 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
374 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
375 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
377 this._patchTimelineHeader();
378 this._dataGrid.sortNodes(this._sortingFunctions.startTime, false);
382 * @param {string} title
383 * @param {string} subtitle
384 * @return {!DocumentFragment}
386 _makeHeaderFragment: function(title, subtitle)
388 var fragment = document.createDocumentFragment();
389 fragment.createTextChild(title);
390 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
391 subtitleDiv.createTextChild(subtitle);
395 _patchTimelineHeader: function()
397 var timelineSorting = document.createElement("select");
399 var option = document.createElement("option");
400 option.value = "startTime";
401 option.label = WebInspector.UIString("Timeline");
402 timelineSorting.appendChild(option);
404 option = document.createElement("option");
405 option.value = "startTime";
406 option.label = WebInspector.UIString("Start Time");
407 timelineSorting.appendChild(option);
409 option = document.createElement("option");
410 option.value = "responseTime";
411 option.label = WebInspector.UIString("Response Time");
412 timelineSorting.appendChild(option);
414 option = document.createElement("option");
415 option.value = "endTime";
416 option.label = WebInspector.UIString("End Time");
417 timelineSorting.appendChild(option);
419 option = document.createElement("option");
420 option.value = "duration";
421 option.label = WebInspector.UIString("Duration");
422 timelineSorting.appendChild(option);
424 option = document.createElement("option");
425 option.value = "latency";
426 option.label = WebInspector.UIString("Latency");
427 timelineSorting.appendChild(option);
429 var header = this._dataGrid.headerTableHeader("timeline");
430 header.replaceChild(timelineSorting, header.firstChild);
432 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
433 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
434 this._timelineSortSelector = timelineSorting;
437 _createSortingFunctions: function()
439 this._sortingFunctions = {};
440 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
441 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
442 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
443 this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
444 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
445 this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
446 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
447 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
448 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
449 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
450 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
451 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
452 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
453 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
454 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
455 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
456 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
457 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
459 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
460 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
462 this._calculators = {};
463 this._calculators.timeline = timeCalculator;
464 this._calculators.startTime = timeCalculator;
465 this._calculators.endTime = timeCalculator;
466 this._calculators.responseTime = timeCalculator;
467 this._calculators.duration = durationCalculator;
468 this._calculators.latency = durationCalculator;
471 _sortItems: function()
473 this._removeAllNodeHighlights();
474 var columnIdentifier = this._dataGrid.sortColumnIdentifier();
475 if (columnIdentifier === "timeline") {
476 this._sortByTimeline();
479 var sortingFunction = this._sortingFunctions[columnIdentifier];
480 if (!sortingFunction)
483 this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
484 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
485 this._timelineSortSelector.selectedIndex = 0;
487 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
488 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
489 column: columnIdentifier,
490 sortOrder: this._dataGrid.sortOrder()
494 _sortByTimeline: function()
496 this._removeAllNodeHighlights();
497 var selectedIndex = this._timelineSortSelector.selectedIndex;
499 selectedIndex = 1; // Sort by start time by default.
500 var selectedOption = this._timelineSortSelector[selectedIndex];
501 var value = selectedOption.value;
503 var sortingFunction = this._sortingFunctions[value];
504 this._dataGrid.sortNodes(sortingFunction);
505 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
506 var calculator = this._calculators[value];
507 this._setCalculator(calculator);
508 if (calculator.startAtZero)
509 this._timelineGrid.hideEventDividers();
511 this._timelineGrid.showEventDividers();
512 this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
515 _createStatusBarItems: function()
517 this._progressBarContainer = document.createElement("div");
518 this._progressBarContainer.className = "status-bar-item";
521 _updateSummaryBar: function()
523 var requestsNumber = this._nodesByRequestId.size();
525 if (!requestsNumber) {
526 if (this._summaryBarElement._isDisplayingWarning)
528 this._summaryBarElement._isDisplayingWarning = true;
529 this._summaryBarElement.removeChildren();
530 this._summaryBarElement.createChild("div", "warning-icon-small");
531 var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
532 this._summaryBarElement.appendChild(document.createTextNode(text));
533 this._summaryBarElement.title = text;
536 delete this._summaryBarElement._isDisplayingWarning;
538 var transferSize = 0;
539 var selectedRequestsNumber = 0;
540 var selectedTransferSize = 0;
543 var nodes = this._nodesByRequestId.values();
544 for (var i = 0; i < nodes.length; ++i) {
545 var request = nodes[i]._request;
546 var requestTransferSize = request.transferSize;
547 transferSize += requestTransferSize;
548 if (!nodes[i]._isFilteredOut) {
549 selectedRequestsNumber++;
550 selectedTransferSize += requestTransferSize;
552 if (request.url === request.target().resourceTreeModel.inspectedPageURL())
553 baseTime = request.startTime;
554 if (request.endTime > maxTime)
555 maxTime = request.endTime;
558 if (selectedRequestsNumber !== requestsNumber) {
559 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
560 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
562 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
563 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
565 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
566 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
567 Number.secondsToString(maxTime - baseTime),
568 Number.secondsToString(this._mainRequestLoadTime - baseTime),
569 Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
571 this._summaryBarElement.textContent = text;
572 this._summaryBarElement.title = text;
575 _scheduleRefresh: function()
577 if (this._needsRefresh)
580 this._needsRefresh = true;
582 if (this.isShowing() && !this._refreshTimeout)
583 this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
586 _updateDividersIfNeeded: function()
590 var timelineOffset = this._dataGrid.columnOffset("timeline");
591 // Position timline grid location.
593 this._timelineGrid.element.style.left = timelineOffset + "px";
595 var calculator = this.calculator();
597 if (!this.isShowing()) {
598 this._scheduleRefresh();
601 calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
602 proceed = this._timelineGrid.updateDividers(calculator);
607 if (calculator.startAtZero || !calculator.computePercentageFromEventTime) {
608 // If our current sorting method starts at zero, that means it shows all
609 // requests starting at the same point, and so onLoad event and DOMContent
610 // event lines really wouldn't make much sense here, so don't render them.
611 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
612 // function defined, we are probably sorting by size, and event times aren't relevant
617 this._timelineGrid.removeEventDividers();
618 if (this._mainRequestLoadTime !== -1) {
619 var percent = calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
621 var loadDivider = document.createElement("div");
622 loadDivider.className = "network-event-divider network-red-divider";
624 var loadDividerPadding = document.createElement("div");
625 loadDividerPadding.className = "network-event-divider-padding";
626 loadDividerPadding.title = WebInspector.UIString("Load event");
627 loadDividerPadding.appendChild(loadDivider);
628 loadDividerPadding.style.left = percent + "%";
629 this._timelineGrid.addEventDivider(loadDividerPadding);
632 if (this._mainRequestDOMContentLoadedTime !== -1) {
633 var percent = calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
635 var domContentLoadedDivider = document.createElement("div");
636 domContentLoadedDivider.className = "network-event-divider network-blue-divider";
638 var domContentLoadedDividerPadding = document.createElement("div");
639 domContentLoadedDividerPadding.className = "network-event-divider-padding";
640 domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event");
641 domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
642 domContentLoadedDividerPadding.style.left = percent + "%";
643 this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
647 _refreshIfNeeded: function()
649 if (this._needsRefresh)
653 _invalidateAllItems: function()
655 var requestIds = this._nodesByRequestId.keys();
656 for (var i = 0; i < requestIds.length; ++i)
657 this._staleRequestIds[requestIds[i]] = true;
661 * @return {!WebInspector.NetworkBaseCalculator}
663 calculator: function()
665 return this._calculator;
669 * @param {!WebInspector.NetworkBaseCalculator} x
671 _setCalculator: function(x)
673 if (!x || this._calculator === x)
676 this._calculator = x;
677 this._calculator.reset();
679 this._invalidateAllItems();
683 _createStatusbarButtons: function()
685 this._recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record Network Log"), "record-profile-status-bar-item");
686 this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
688 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
689 this._clearButton.addEventListener("click", this._reset, this);
691 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
692 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
693 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
695 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
696 this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
698 this._disableCacheCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Disable cache"));
699 WebInspector.SettingsUI.bindCheckbox(this._disableCacheCheckbox.inputElement, WebInspector.settings.cacheDisabled);
700 this._disableCacheCheckbox.element.title = WebInspector.UIString("Disable cache (while DevTools is open).");
704 * @param {!WebInspector.Event} event
706 _loadEventFired: function(event)
708 if (!this._recordButton.toggled)
711 this._mainRequestLoadTime = event.data || -1;
712 // Schedule refresh to update boundaries and draw the new line.
713 this._scheduleRefresh();
717 * @param {!WebInspector.Event} event
719 _domContentLoadedEventFired: function(event)
721 if (!this._recordButton.toggled)
723 this._mainRequestDOMContentLoadedTime = event.data || -1;
724 // Schedule refresh to update boundaries and draw the new line.
725 this._scheduleRefresh();
730 this._refreshIfNeeded();
735 this._popoverHelper.hidePopover();
740 this._needsRefresh = false;
741 if (this._refreshTimeout) {
742 clearTimeout(this._refreshTimeout);
743 delete this._refreshTimeout;
746 this._removeAllNodeHighlights();
747 var boundariesChanged = false;
748 var calculator = this.calculator();
749 if (calculator.updateBoundariesForEventTime) {
750 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
751 boundariesChanged = calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
754 var dataGrid = this._dataGrid;
755 var rootNode = dataGrid.rootNode();
756 var nodesToInsert = [];
757 for (var requestId in this._staleRequestIds) {
758 var node = this._nodesByRequestId.get(requestId);
761 if (!node._isFilteredOut)
762 rootNode.removeChild(node);
763 node._isFilteredOut = !this._applyFilter(node);
764 if (!node._isFilteredOut)
765 nodesToInsert.push(node);
768 for (var i = 0; i < nodesToInsert.length; ++i) {
769 var node = nodesToInsert[i];
770 var request = node._request;
772 dataGrid.insertChild(node);
773 node._isMatchingSearchQuery = this._matchRequest(request);
774 if (calculator.updateBoundaries(request))
775 boundariesChanged = true;
778 this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
780 if (boundariesChanged) {
781 // The boundaries changed, so all item graphs are stale.
782 this._updateDividersIfNeeded();
783 var nodes = this._nodesByRequestId.values();
784 for (var i = 0; i < nodes.length; ++i)
785 nodes[i].refreshGraph(calculator);
788 this._staleRequestIds = {};
789 this._updateSummaryBar();
792 _onRecordButtonClicked: function()
794 if (!this._recordButton.toggled)
796 this._recordButton.toggled = !this._recordButton.toggled;
801 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
803 this._clearSearchMatchedList();
804 if (this._popoverHelper)
805 this._popoverHelper.hidePopover();
807 if (this._calculator)
808 this._calculator.reset();
810 var nodes = this._nodesByRequestId.values();
811 for (var i = 0; i < nodes.length; ++i)
814 this._nodesByRequestId.clear();
815 this._staleRequestIds = {};
816 this._resetSuggestionBuilder();
818 if (this._dataGrid) {
819 this._dataGrid.rootNode().removeChildren();
820 this._updateDividersIfNeeded();
821 this._updateSummaryBar();
824 this._mainRequestLoadTime = -1;
825 this._mainRequestDOMContentLoadedTime = -1;
829 * @param {!WebInspector.Event} event
831 _onRequestStarted: function(event)
833 if (this._recordButton.toggled) {
834 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
835 this._appendRequest(request);
840 * @param {!WebInspector.NetworkRequest} request
842 _appendRequest: function(request)
844 var node = new WebInspector.NetworkDataGridNode(this, request);
846 // In case of redirect request id is reassigned to a redirected
847 // request and we need to update _nodesByRequestId and search results.
848 var originalRequestNode = this._nodesByRequestId.get(request.requestId);
849 if (originalRequestNode)
850 this._nodesByRequestId.put(originalRequestNode._request.requestId, originalRequestNode);
851 this._nodesByRequestId.put(request.requestId, node);
853 // Pull all the redirects of the main request upon commit load.
854 if (request.redirects) {
855 for (var i = 0; i < request.redirects.length; ++i)
856 this._refreshRequest(request.redirects[i]);
859 this._refreshRequest(request);
863 * @param {!WebInspector.Event} event
865 _onRequestUpdated: function(event)
867 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
868 this._refreshRequest(request);
872 * @param {!WebInspector.NetworkRequest} request
874 _refreshRequest: function(request)
876 if (!this._nodesByRequestId.get(request.requestId))
879 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Domain, request.domain);
880 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Method, request.requestMethod);
881 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.MimeType, request.mimeType);
882 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.StatusCode, "" + request.statusCode);
884 var responseHeaders = request.responseHeaders;
885 for (var i = 0, l = responseHeaders.length; i < l; ++i)
886 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.HasResponseHeader, responseHeaders[i].name);
887 var cookies = request.responseCookies;
888 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
889 var cookie = cookies[i];
890 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieDomain, cookie.domain());
891 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieName, cookie.name());
892 this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieValue, cookie.value());
895 this._staleRequestIds[request.requestId] = true;
896 this._scheduleRefresh();
900 * @param {!WebInspector.Event} event
902 _willReloadPage: function(event)
904 this._recordButton.toggled = true;
905 if (!this._preserveLogCheckbox.checked())
910 * @param {!WebInspector.Event} event
912 _mainFrameNavigated: function(event)
914 if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
917 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
918 var loaderId = frame.loaderId;
920 // Pick provisional load requests.
921 var requestsToPick = [];
922 var requests = frame.target().networkLog.requests;
923 for (var i = 0; i < requests.length; ++i) {
924 var request = requests[i];
925 if (request.loaderId === loaderId)
926 requestsToPick.push(request);
931 for (var i = 0; i < requestsToPick.length; ++i)
932 this._appendRequest(requestsToPick[i]);
936 * @param {boolean} detailed
938 switchViewMode: function(detailed)
940 if (this._detailedMode === detailed)
942 this._detailedMode = detailed;
945 if (this._dataGrid.selectedNode)
946 this._dataGrid.selectedNode.selected = false;
948 this._removeAllNodeHighlights();
949 this._popoverHelper.hidePopover();
952 this.element.classList.toggle("brief-mode", !detailed);
953 this._updateColumns();
956 _toggleLargerRequests: function()
958 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
959 this._updateRowsSize();
965 rowHeight: function()
967 return this._rowHeight;
970 _updateRowsSize: function()
972 var largeRows = this.usesLargeRows();
973 this._largerRequestsButton.toggled = largeRows;
974 this._rowHeight = largeRows ? 41 : 21;
975 this._largerRequestsButton.title = WebInspector.UIString(largeRows ? "Use small resource rows." : "Use large resource rows.");
976 this._dataGrid.element.classList.toggle("small", !largeRows);
977 this._timelineGrid.element.classList.toggle("small", !largeRows);
978 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: largeRows });
982 * @param {!Element} element
983 * @param {!Event} event
984 * @return {!Element|!AnchorBox|undefined}
986 _getPopoverAnchor: function(element, event)
988 if (!this._allowPopover)
990 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
991 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
993 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
994 if (anchor && anchor.request && anchor.request.initiator)
999 * @param {!Element} anchor
1000 * @param {!WebInspector.Popover} popover
1002 _showPopover: function(anchor, popover)
1005 if (anchor.classList.contains("network-script-initiated"))
1006 content = this._generateScriptInitiatedPopoverContent(anchor.request);
1008 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
1009 popover.show(content, anchor);
1012 _onHidePopover: function()
1014 this._linkifier.reset();
1018 * @param {!WebInspector.NetworkRequest} request
1019 * @return {!Element}
1021 _generateScriptInitiatedPopoverContent: function(request)
1023 var framesTable = document.createElementWithClass("table", "network-stack-trace");
1026 * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
1027 * @this {WebInspector.NetworkLogView}
1029 function appendStackTrace(stackTrace)
1031 for (var i = 0; i < stackTrace.length; ++i) {
1032 var stackFrame = stackTrace[i];
1033 var row = document.createElement("tr");
1034 row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
1035 row.createChild("td").textContent = " @ ";
1036 row.createChild("td").appendChild(this._linkifier.linkifyConsoleCallFrame(request.target(), stackFrame));
1037 framesTable.appendChild(row);
1041 appendStackTrace.call(this, request.initiator.stackTrace);
1043 var asyncStackTrace = request.initiator.asyncStackTrace;
1044 while (asyncStackTrace) {
1045 var callFrames = asyncStackTrace.callFrames;
1046 if (!callFrames || !callFrames.length)
1048 var row = framesTable.createChild("tr");
1049 row.createChild("td", "network-async-trace-description").textContent = WebInspector.asyncStackTraceLabel(asyncStackTrace.description);
1050 row.createChild("td");
1051 row.createChild("td");
1052 appendStackTrace.call(this, callFrames);
1053 asyncStackTrace = asyncStackTrace.asyncStackTrace;
1059 _updateColumns: function()
1061 var detailedMode = !!this._detailedMode;
1062 var visibleColumns = {"name": true};
1064 visibleColumns["timeline"] = true;
1065 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1066 for (var columnIdentifier in columnsVisibility)
1067 visibleColumns[columnIdentifier] = columnsVisibility[columnIdentifier];
1070 this._dataGrid.setColumnsVisiblity(visibleColumns);
1074 * @param {string} columnIdentifier
1076 _toggleColumnVisibility: function(columnIdentifier)
1078 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1079 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
1080 this._coulmnsVisibilitySetting.set(columnsVisibility);
1082 this._updateColumns();
1086 * @return {!Array.<string>}
1088 _getConfigurableColumnIDs: function()
1090 if (this._configurableColumnIDs)
1091 return this._configurableColumnIDs;
1093 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1094 function compare(id1, id2)
1096 return columnTitles[id1].compareTo(columnTitles[id2]);
1099 var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
1100 this._configurableColumnIDs = columnIDs.sort(compare);
1101 return this._configurableColumnIDs;
1105 * @param {!Event} event
1107 _contextMenu: function(event)
1109 var contextMenu = new WebInspector.ContextMenu(event);
1111 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
1112 var columnsVisibility = this._coulmnsVisibilitySetting.get();
1113 var columnIDs = this._getConfigurableColumnIDs();
1114 var columnTitles = WebInspector.NetworkLogView._columnTitles;
1115 for (var i = 0; i < columnIDs.length; ++i) {
1116 var columnIdentifier = columnIDs[i];
1117 contextMenu.appendCheckboxItem(columnTitles[columnIdentifier], this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1123 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1124 var request = gridNode && gridNode._request;
1127 * @param {string} url
1129 function openResourceInNewTab(url)
1131 InspectorFrontendHost.openInNewTab(url);
1135 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), openResourceInNewTab.bind(null, request.url));
1136 contextMenu.appendSeparator();
1137 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1138 if (request.requestHeadersText())
1139 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1140 if (request.responseHeadersText)
1141 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1142 if (request.finished)
1143 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response" : "Copy Response"), this._copyResponse.bind(this, request));
1144 contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1146 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1148 contextMenu.appendSeparator();
1149 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1151 contextMenu.appendSeparator();
1152 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1153 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1155 if (request && request.type === WebInspector.resourceTypes.XHR) {
1156 contextMenu.appendSeparator();
1157 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
1158 contextMenu.appendSeparator();
1165 * @param {string} requestId
1167 _replayXHR: function(requestId)
1169 NetworkAgent.replayXHR(requestId);
1172 _harRequests: function()
1174 var requests = this._nodesByRequestId.values().map(function(node) { return node._request; });
1175 var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1176 httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1177 return httpRequests.filter(WebInspector.NetworkLogView.NonDevToolsRequestsFilter);
1180 _copyAll: function()
1183 log: (new WebInspector.HARLog(this._harRequests())).build()
1185 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1189 * @param {!WebInspector.NetworkRequest} request
1191 _copyLocation: function(request)
1193 InspectorFrontendHost.copyText(request.url);
1197 * @param {!WebInspector.NetworkRequest} request
1199 _copyRequestHeaders: function(request)
1201 InspectorFrontendHost.copyText(request.requestHeadersText());
1205 * @param {!WebInspector.NetworkRequest} request
1207 _copyResponse: function(request)
1210 * @param {?string} content
1212 function callback(content)
1214 if (request.contentEncoded)
1215 content = request.asDataURL();
1216 InspectorFrontendHost.copyText(content || "");
1218 request.requestContent(callback);
1222 * @param {!WebInspector.NetworkRequest} request
1224 _copyResponseHeaders: function(request)
1226 InspectorFrontendHost.copyText(request.responseHeadersText);
1230 * @param {!WebInspector.NetworkRequest} request
1232 _copyCurlCommand: function(request)
1234 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1237 _exportAll: function()
1239 var filename = WebInspector.resourceTreeModel.inspectedPageDomain() + ".har";
1240 var stream = new WebInspector.FileOutputStream();
1241 stream.open(filename, openCallback.bind(this));
1244 * @param {boolean} accepted
1245 * @this {WebInspector.NetworkLogView}
1247 function openCallback(accepted)
1251 var progressIndicator = new WebInspector.ProgressIndicator();
1252 this._progressBarContainer.appendChild(progressIndicator.element);
1253 var harWriter = new WebInspector.HARWriter();
1254 harWriter.write(stream, this._harRequests(), progressIndicator);
1258 _clearBrowserCache: function()
1260 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1261 NetworkAgent.clearBrowserCache();
1264 _clearBrowserCookies: function()
1266 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1267 NetworkAgent.clearBrowserCookies();
1271 * @param {!WebInspector.NetworkRequest} request
1274 _matchRequest: function(request)
1276 var re = this._searchRegExp;
1279 return re.test(request.name()) || re.test(request.path());
1282 _clearSearchMatchedList: function()
1284 this._matchedRequestCount = -1;
1285 this._currentMatchedRequestNode = null;
1286 this._removeAllHighlights();
1289 _removeAllHighlights: function()
1291 this._removeAllNodeHighlights();
1292 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1293 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1294 this._highlightedSubstringChanges = [];
1299 * @param {boolean} reveal
1301 _highlightNthMatchedRequestForSearch: function(n, reveal)
1303 this._removeAllHighlights();
1305 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1306 var nodes = this._dataGrid.rootNode().children;
1309 for (var i = 0; i < nodes.length; ++i) {
1310 if (nodes[i]._isMatchingSearchQuery) {
1311 if (matchCount === n) {
1319 this._currentMatchedRequestNode = null;
1323 var request = node._request;
1324 var regExp = this._searchRegExp;
1325 var nameMatched = request.name().match(regExp);
1326 var pathMatched = request.path().match(regExp);
1327 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1328 this._toggleLargerRequests();
1330 WebInspector.Revealer.reveal(request);
1331 var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
1332 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1334 this._currentMatchedRequestNode = node;
1335 this._currentMatchedRequestIndex = n;
1336 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
1340 * @param {string} query
1341 * @param {boolean} shouldJump
1342 * @param {boolean=} jumpBackwards
1344 performSearch: function(query, shouldJump, jumpBackwards)
1346 var currentMatchedRequestNode = this._currentMatchedRequestNode;
1347 this._clearSearchMatchedList();
1348 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1350 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1351 var nodes = this._dataGrid.rootNode().children;
1352 for (var i = 0; i < nodes.length; ++i)
1353 nodes[i]._isMatchingSearchQuery = this._matchRequest(nodes[i]._request);
1354 var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
1355 if (!newMatchedRequestIndex && jumpBackwards)
1356 newMatchedRequestIndex = this._matchedRequestCount - 1;
1357 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
1361 * @param {?WebInspector.NetworkDataGridNode} node
1364 _updateMatchCountAndFindMatchIndex: function(node)
1366 /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1367 var nodes = this._dataGrid.rootNode().children;
1370 for (var i = 0; i < nodes.length; ++i) {
1371 if (!nodes[i]._isMatchingSearchQuery)
1373 if (node === nodes[i])
1374 matchIndex = matchCount;
1377 if (this._matchedRequestCount !== matchCount) {
1378 this._matchedRequestCount = matchCount;
1379 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
1385 * @param {number} index
1388 _normalizeSearchResultIndex: function(index)
1390 return (index + this._matchedRequestCount) % this._matchedRequestCount;
1394 * @param {!WebInspector.NetworkDataGridNode} node
1397 _applyFilter: function(node)
1399 var request = node._request;
1400 if (!this._resourceTypeFilterUI.accept(request.type.name()))
1402 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1404 for (var i = 0; i < this._filters.length; ++i) {
1405 if (!this._filters[i](request))
1412 * @param {string} query
1414 _parseFilterQuery: function(query)
1416 var parsedQuery = this._suggestionBuilder.parseQuery(query);
1417 this._filters = parsedQuery.text.map(this._createTextFilter);
1418 for (var key in parsedQuery.filters) {
1419 var filterType = /** @type {!WebInspector.NetworkPanel.FilterType} */ (key);
1420 this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1425 * @param {string} text
1426 * @return {!WebInspector.NetworkLogView.Filter}
1428 _createTextFilter: function(text)
1430 var regexp = new RegExp(text.escapeForRegExp(), "i");
1431 return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1435 * @param {!WebInspector.NetworkPanel.FilterType} type
1436 * @param {string} value
1437 * @return {!WebInspector.NetworkLogView.Filter}
1439 _createFilter: function(type, value) {
1441 case WebInspector.NetworkPanel.FilterType.Domain:
1442 return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1444 case WebInspector.NetworkPanel.FilterType.HasResponseHeader:
1445 return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1447 case WebInspector.NetworkPanel.FilterType.Method:
1448 return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1450 case WebInspector.NetworkPanel.FilterType.MimeType:
1451 return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1453 case WebInspector.NetworkPanel.FilterType.SetCookieDomain:
1454 return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1456 case WebInspector.NetworkPanel.FilterType.SetCookieName:
1457 return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1459 case WebInspector.NetworkPanel.FilterType.SetCookieValue:
1460 return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1462 case WebInspector.NetworkPanel.FilterType.StatusCode:
1463 return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1465 return this._createTextFilter(type + ":" + value);
1468 _filterRequests: function()
1470 this._removeAllHighlights();
1471 this._invalidateAllItems();
1475 jumpToPreviousSearchResult: function()
1477 if (!this._matchedRequestCount)
1479 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1480 this._highlightNthMatchedRequestForSearch(index, true);
1483 jumpToNextSearchResult: function()
1485 if (!this._matchedRequestCount)
1487 var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1488 this._highlightNthMatchedRequestForSearch(index, true);
1491 searchCanceled: function()
1493 delete this._searchRegExp;
1494 this._clearSearchMatchedList();
1495 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1499 * @param {!WebInspector.NetworkRequest} request
1501 revealAndHighlightRequest: function(request)
1503 this._removeAllNodeHighlights();
1505 var node = this._nodesByRequestId.get(request.requestId);
1508 this._highlightNode(node);
1512 _removeAllNodeHighlights: function()
1514 if (this._highlightedNode) {
1515 this._highlightedNode.element().classList.remove("highlighted-row");
1516 delete this._highlightedNode;
1521 * @param {!WebInspector.NetworkDataGridNode} node
1523 _highlightNode: function(node)
1525 WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
1526 this._highlightedNode = node;
1530 * @param {!WebInspector.NetworkRequest} request
1533 _generateCurlCommand: function(request)
1535 var command = ["curl"];
1536 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1537 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1539 function escapeStringWin(str)
1541 /* Replace quote by double quote (but not by \") because it is
1542 recognized by both cmd.exe and MS Crt arguments parser.
1544 Replace % by "%" because it could be expanded to an environment
1545 variable value. So %% becomes "%""%". Even if an env variable ""
1546 (2 doublequotes) is declared, the cmd.exe will not
1547 substitute it with its value.
1549 Replace each backslash with double backslash to make sure
1550 MS Crt arguments parser won't collapse them.
1552 Replace new line outside of quotes since cmd.exe doesn't let
1555 return "\"" + str.replace(/"/g, "\"\"")
1556 .replace(/%/g, "\"%\"")
1557 .replace(/\\/g, "\\\\")
1558 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1561 function escapeStringPosix(str)
1563 function escapeCharacter(x)
1565 var code = x.charCodeAt(0);
1567 // Add leading zero when needed to not care about the next character.
1568 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1570 code = code.toString(16);
1571 return "\\u" + ("0000" + code).substr(code.length, 4);
1574 if (/[^\x20-\x7E]|\'/.test(str)) {
1575 // Use ANSI-C quoting syntax.
1576 return "$\'" + str.replace(/\\/g, "\\\\")
1577 .replace(/\'/g, "\\\'")
1578 .replace(/\n/g, "\\n")
1579 .replace(/\r/g, "\\r")
1580 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1582 // Use single quote syntax.
1583 return "'" + str + "'";
1587 // cURL command expected to run on the same platform that DevTools run
1588 // (it may be different from the inspected page platform).
1589 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1591 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1593 var inferredMethod = "GET";
1595 var requestContentType = request.requestContentType();
1596 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1597 data.push("--data");
1598 data.push(escapeString(request.requestFormData));
1599 ignoredHeaders["content-length"] = true;
1600 inferredMethod = "POST";
1601 } else if (request.requestFormData) {
1602 data.push("--data-binary");
1603 data.push(escapeString(request.requestFormData));
1604 ignoredHeaders["content-length"] = true;
1605 inferredMethod = "POST";
1608 if (request.requestMethod !== inferredMethod) {
1610 command.push(request.requestMethod);
1613 var requestHeaders = request.requestHeaders();
1614 for (var i = 0; i < requestHeaders.length; i++) {
1615 var header = requestHeaders[i];
1616 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1617 if (name.toLowerCase() in ignoredHeaders)
1620 command.push(escapeString(name + ": " + header.value));
1622 command = command.concat(data);
1623 command.push("--compressed");
1624 return command.join(" ");
1627 __proto__: WebInspector.VBox.prototype
1630 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1631 WebInspector.NetworkLogView.Filter;
1634 * @param {!RegExp} regex
1635 * @param {!WebInspector.NetworkRequest} request
1638 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1640 return regex.test(request.name()) || regex.test(request.path());
1644 * @param {string} value
1645 * @param {!WebInspector.NetworkRequest} request
1648 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1650 return request.domain === value;
1654 * @param {string} value
1655 * @param {!WebInspector.NetworkRequest} request
1658 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1660 return request.responseHeaderValue(value) !== undefined;
1664 * @param {string} value
1665 * @param {!WebInspector.NetworkRequest} request
1668 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1670 return request.requestMethod === value;
1674 * @param {string} value
1675 * @param {!WebInspector.NetworkRequest} request
1678 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1680 return request.mimeType === value;
1684 * @param {string} value
1685 * @param {!WebInspector.NetworkRequest} request
1688 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1690 var cookies = request.responseCookies;
1691 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1692 if (cookies[i].domain() === value)
1699 * @param {string} value
1700 * @param {!WebInspector.NetworkRequest} request
1703 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1705 var cookies = request.responseCookies;
1706 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1707 if (cookies[i].name() === value)
1714 * @param {string} value
1715 * @param {!WebInspector.NetworkRequest} request
1718 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1720 var cookies = request.responseCookies;
1721 for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1722 if (cookies[i].value() === value)
1729 * @param {string} value
1730 * @param {!WebInspector.NetworkRequest} request
1733 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1735 return ("" + request.statusCode) === value;
1739 * @param {!WebInspector.NetworkRequest} request
1742 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1744 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1748 * @param {!WebInspector.NetworkRequest} request
1751 WebInspector.NetworkLogView.NonDevToolsRequestsFilter = function(request)
1753 return !WebInspector.NetworkManager.hasDevToolsRequestHeader(request);
1757 * @param {!WebInspector.NetworkRequest} request
1760 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1762 return request.finished;
1765 WebInspector.NetworkLogView.EventTypes = {
1766 ViewCleared: "ViewCleared",
1767 RowSizeChanged: "RowSizeChanged",
1768 RequestSelected: "RequestSelected",
1769 SearchCountUpdated: "SearchCountUpdated",
1770 SearchIndexUpdated: "SearchIndexUpdated"
1775 * @implements {WebInspector.ContextMenu.Provider}
1776 * @implements {WebInspector.Searchable}
1777 * @extends {WebInspector.Panel}
1779 WebInspector.NetworkPanel = function()
1781 WebInspector.Panel.call(this, "network");
1782 this.registerRequiredCSS("networkPanel.css");
1784 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1785 this._filterBar = new WebInspector.FilterBar();
1786 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1787 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1788 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1789 this._filterBar.setName("networkPanel");
1791 this._searchableView = new WebInspector.SearchableView(this);
1792 this._searchableView.show(this.element);
1793 this._contentsElement = this._searchableView.element;
1795 this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1796 this._splitView.show(this._contentsElement);
1797 this._splitView.hideMain();
1799 var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisibility;
1800 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1801 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1802 var columnsVisibility = {};
1803 for (var columnId in defaultColumnsVisibility)
1804 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1805 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1807 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1808 this._networkLogView.show(this._splitView.sidebarElement());
1810 var viewsContainerView = new WebInspector.VBox();
1811 this._viewsContainerElement = viewsContainerView.element;
1812 this._viewsContainerElement.id = "network-views";
1813 if (!this._networkLogView.usesLargeRows())
1814 this._viewsContainerElement.classList.add("small");
1815 viewsContainerView.show(this._splitView.mainElement());
1817 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1818 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1819 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1820 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1821 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1823 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1824 this._closeButtonElement.id = "network-close-button";
1825 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1826 this._viewsContainerElement.appendChild(this._closeButtonElement);
1828 var statusBarItems = this._networkLogView.statusBarItems();
1829 for (var i = 0; i < statusBarItems.length; ++i)
1830 this._panelStatusBarElement.appendChild(statusBarItems[i]);
1833 * @this {WebInspector.NetworkPanel}
1834 * @return {?WebInspector.SourceFrame}
1836 function sourceFrameGetter()
1838 return this._networkItemView.currentSourceFrame();
1840 WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1843 /** @enum {string} */
1844 WebInspector.NetworkPanel.FilterType = {
1846 HasResponseHeader: "HasResponseHeader",
1848 MimeType: "MimeType",
1849 SetCookieDomain: "SetCookieDomain",
1850 SetCookieName: "SetCookieName",
1851 SetCookieValue: "SetCookieValue",
1852 StatusCode: "StatusCode"
1855 /** @type {!Array.<string>} */
1856 WebInspector.NetworkPanel._searchKeys = Object.values(WebInspector.NetworkPanel.FilterType);
1858 WebInspector.NetworkPanel.prototype = {
1860 * @param {!WebInspector.Event} event
1862 _onFiltersToggled: function(event)
1864 var toggled = /** @type {boolean} */ (event.data);
1865 this._filtersContainer.classList.toggle("hidden", !toggled);
1866 this.element.classList.toggle("filters-toggled", toggled);
1871 * @return {!Array.<!Element>}
1873 elementsToRestoreScrollPositionsFor: function()
1875 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1879 * @return {!WebInspector.SearchableView}
1881 searchableView: function()
1883 return this._searchableView;
1886 // FIXME: only used by the layout tests, should not be exposed.
1889 this._networkLogView._reset();
1893 * @param {!KeyboardEvent} event
1895 handleShortcut: function(event)
1897 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1898 this._toggleGridMode();
1899 event.handled = true;
1903 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1906 wasShown: function()
1908 WebInspector.Panel.prototype.wasShown.call(this);
1912 * @param {!WebInspector.NetworkRequest} request
1914 revealAndHighlightRequest: function(request)
1916 this._toggleGridMode();
1918 this._networkLogView.revealAndHighlightRequest(request);
1922 * @param {!WebInspector.Event} event
1924 _onViewCleared: function(event)
1926 this._closeVisibleRequest();
1927 this._toggleGridMode();
1928 this._viewsContainerElement.removeChildren();
1929 this._viewsContainerElement.appendChild(this._closeButtonElement);
1933 * @param {!WebInspector.Event} event
1935 _onRowSizeChanged: function(event)
1937 this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1941 * @param {!WebInspector.Event} event
1943 _onSearchCountUpdated: function(event)
1945 var count = /** @type {number} */ (event.data);
1946 this._searchableView.updateSearchMatchesCount(count);
1950 * @param {!WebInspector.Event} event
1952 _onSearchIndexUpdated: function(event)
1954 var index = /** @type {number} */ (event.data);
1955 this._searchableView.updateCurrentMatchIndex(index);
1959 * @param {!WebInspector.Event} event
1961 _onRequestSelected: function(event)
1963 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
1964 this._showRequest(request);
1968 * @param {?WebInspector.NetworkRequest} request
1970 _showRequest: function(request)
1975 this._toggleViewingRequestMode();
1977 if (this._networkItemView) {
1978 this._networkItemView.detach();
1979 delete this._networkItemView;
1982 var view = new WebInspector.NetworkItemView(request);
1983 view.show(this._viewsContainerElement);
1984 this._networkItemView = view;
1987 _closeVisibleRequest: function()
1989 this.element.classList.remove("viewing-resource");
1991 if (this._networkItemView) {
1992 this._networkItemView.detach();
1993 delete this._networkItemView;
1997 _toggleGridMode: function()
1999 if (this._viewingRequestMode) {
2000 this._viewingRequestMode = false;
2001 this.element.classList.remove("viewing-resource");
2002 this._splitView.hideMain();
2005 this._networkLogView.switchViewMode(true);
2006 this._networkLogView.setAllowPopover(true);
2007 this._networkLogView._allowRequestSelection = false;
2010 _toggleViewingRequestMode: function()
2012 if (this._viewingRequestMode)
2014 this._viewingRequestMode = true;
2016 this.element.classList.add("viewing-resource");
2017 this._splitView.showBoth();
2018 this._networkLogView.setAllowPopover(false);
2019 this._networkLogView._allowRequestSelection = true;
2020 this._networkLogView.switchViewMode(false);
2024 * @param {string} query
2025 * @param {boolean} shouldJump
2026 * @param {boolean=} jumpBackwards
2028 performSearch: function(query, shouldJump, jumpBackwards)
2030 this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
2033 jumpToPreviousSearchResult: function()
2035 this._networkLogView.jumpToPreviousSearchResult();
2038 jumpToNextSearchResult: function()
2040 this._networkLogView.jumpToNextSearchResult();
2043 searchCanceled: function()
2045 this._networkLogView.searchCanceled();
2049 * @param {!Event} event
2050 * @param {!WebInspector.ContextMenu} contextMenu
2051 * @param {!Object} target
2052 * @this {WebInspector.NetworkPanel}
2054 appendApplicableItems: function(event, contextMenu, target)
2057 * @this {WebInspector.NetworkPanel}
2059 function reveal(request)
2061 WebInspector.inspectorView.setCurrentPanel(this);
2062 this.revealAndHighlightRequest(request);
2066 * @this {WebInspector.NetworkPanel}
2068 function appendRevealItem(request)
2070 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
2071 contextMenu.appendItem(revealText, reveal.bind(this, request));
2074 if (target instanceof WebInspector.Resource) {
2075 var resource = /** @type {!WebInspector.Resource} */ (target);
2076 if (resource.request)
2077 appendRevealItem.call(this, resource.request);
2080 if (target instanceof WebInspector.UISourceCode) {
2081 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
2082 var resource = WebInspector.resourceForURL(uiSourceCode.url);
2083 if (resource && resource.request)
2084 appendRevealItem.call(this, resource.request);
2088 if (!(target instanceof WebInspector.NetworkRequest))
2090 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2091 if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2094 appendRevealItem.call(this, request);
2097 __proto__: WebInspector.Panel.prototype
2102 * @implements {WebInspector.ContextMenu.Provider}
2104 WebInspector.NetworkPanel.ContextMenuProvider = function()
2108 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2110 * @param {!Event} event
2111 * @param {!WebInspector.ContextMenu} contextMenu
2112 * @param {!Object} target
2114 appendApplicableItems: function(event, contextMenu, target)
2116 WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2122 * @implements {WebInspector.Revealer}
2124 WebInspector.NetworkPanel.RequestRevealer = function()
2128 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2130 * @param {!Object} request
2131 * @param {number=} lineNumber
2133 reveal: function(request, lineNumber)
2135 if (request instanceof WebInspector.NetworkRequest) {
2136 var panel = /** @type {?WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network"));
2138 panel.revealAndHighlightRequest(request);
2145 * @implements {WebInspector.TimelineGrid.Calculator}
2147 WebInspector.NetworkBaseCalculator = function()
2151 WebInspector.NetworkBaseCalculator.prototype = {
2153 * @param {number} time
2156 computePosition: function(time)
2158 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2162 * @return {!{start: number, middle: number, end: number}}
2164 computeBarGraphPercentages: function(item)
2166 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
2170 * @return {!{left: string, right: string, tooltip: string}}
2172 computeBarGraphLabels: function(item)
2174 const label = this.formatTime(this._value(item));
2175 return {left: label, right: label, tooltip: label};
2181 boundarySpan: function()
2183 return this._maximumBoundary - this._minimumBoundary;
2189 updateBoundaries: function(item)
2191 this._minimumBoundary = 0;
2193 var value = this._value(item);
2194 if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
2195 this._maximumBoundary = value;
2203 delete this._minimumBoundary;
2204 delete this._maximumBoundary;
2210 maximumBoundary: function()
2212 return this._maximumBoundary;
2218 minimumBoundary: function()
2220 return this._minimumBoundary;
2226 zeroTime: function()
2228 return this._minimumBoundary;
2234 _value: function(item)
2240 * @param {number} value
2241 * @param {number=} precision
2244 formatTime: function(value, precision)
2246 return value.toString();
2250 * @param {number} clientWidth
2252 setDisplayWindow: function(clientWidth)
2254 this._workingArea = clientWidth;
2260 paddingLeft: function()
2268 * @extends {WebInspector.NetworkBaseCalculator}
2270 WebInspector.NetworkTimeCalculator = function(startAtZero)
2272 WebInspector.NetworkBaseCalculator.call(this);
2273 this.startAtZero = startAtZero;
2276 WebInspector.NetworkTimeCalculator.prototype = {
2278 * @param {!WebInspector.NetworkRequest} request
2279 * @return {!{start: number, middle: number, end: number}}
2281 computeBarGraphPercentages: function(request)
2283 if (request.startTime !== -1)
2284 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2288 if (request.responseReceivedTime !== -1)
2289 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2291 var middle = (this.startAtZero ? start : 100);
2293 if (request.endTime !== -1)
2294 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2296 var end = (this.startAtZero ? middle : 100);
2298 if (this.startAtZero) {
2304 return {start: start, middle: middle, end: end};
2310 computePercentageFromEventTime: function(eventTime)
2312 // This function computes a percentage in terms of the total loading time
2313 // of a specific event. If startAtZero is set, then this is useless, and we
2314 // want to return 0.
2315 if (eventTime !== -1 && !this.startAtZero)
2316 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2324 updateBoundariesForEventTime: function(eventTime)
2326 if (eventTime === -1 || this.startAtZero)
2329 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2330 this._maximumBoundary = eventTime;
2337 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2339 computeBarGraphLabels: function(request)
2341 var rightLabel = "";
2342 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2343 rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2345 var hasLatency = request.latency > 0;
2347 var leftLabel = Number.secondsToString(request.latency);
2349 var leftLabel = rightLabel;
2352 return {left: leftLabel, right: rightLabel};
2354 if (hasLatency && rightLabel) {
2355 var total = Number.secondsToString(request.duration);
2356 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
2357 } else if (hasLatency)
2358 var tooltip = WebInspector.UIString("%s latency", leftLabel);
2359 else if (rightLabel)
2360 var tooltip = WebInspector.UIString("%s download", rightLabel);
2363 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
2364 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2370 updateBoundaries: function(request)
2372 var didChange = false;
2375 if (this.startAtZero)
2378 lowerBound = this._lowerBound(request);
2380 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2381 this._minimumBoundary = lowerBound;
2385 var upperBound = this._upperBound(request);
2386 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2387 this._maximumBoundary = upperBound;
2397 formatTime: function(value)
2399 return Number.secondsToString(value);
2403 * @param {!WebInspector.NetworkRequest} request
2405 _lowerBound: function(request)
2411 * @param {!WebInspector.NetworkRequest} request
2413 _upperBound: function(request)
2418 __proto__: WebInspector.NetworkBaseCalculator.prototype
2423 * @extends {WebInspector.NetworkTimeCalculator}
2425 WebInspector.NetworkTransferTimeCalculator = function()
2427 WebInspector.NetworkTimeCalculator.call(this, false);
2430 WebInspector.NetworkTransferTimeCalculator.prototype = {
2432 * @param {number} value
2435 formatTime: function(value)
2437 return Number.secondsToString(value - this.zeroTime());
2441 * @param {!WebInspector.NetworkRequest} request
2443 _lowerBound: function(request)
2445 return request.startTime;
2449 * @param {!WebInspector.NetworkRequest} request
2451 _upperBound: function(request)
2453 return request.endTime;
2456 __proto__: WebInspector.NetworkTimeCalculator.prototype
2461 * @extends {WebInspector.NetworkTimeCalculator}
2463 WebInspector.NetworkTransferDurationCalculator = function()
2465 WebInspector.NetworkTimeCalculator.call(this, true);
2468 WebInspector.NetworkTransferDurationCalculator.prototype = {
2470 * @param {number} value
2473 formatTime: function(value)
2475 return Number.secondsToString(value);
2479 * @param {!WebInspector.NetworkRequest} request
2481 _upperBound: function(request)
2483 return request.duration;
2486 __proto__: WebInspector.NetworkTimeCalculator.prototype
2491 * @extends {WebInspector.SortableDataGridNode}
2492 * @param {!WebInspector.NetworkLogView} parentView
2493 * @param {!WebInspector.NetworkRequest} request
2495 WebInspector.NetworkDataGridNode = function(parentView, request)
2497 WebInspector.SortableDataGridNode.call(this, {});
2498 this._parentView = parentView;
2499 this._request = request;
2500 this._linkifier = new WebInspector.Linkifier();
2501 this._isFilteredOut = true;
2502 this._isMatchingSearchQuery = false;
2505 WebInspector.NetworkDataGridNode.prototype = {
2510 nodeSelfHeight: function() {
2511 return this._parentView.rowHeight();
2515 createCells: function()
2517 this._nameCell = null;
2518 this._timelineCell = null;
2520 this._element.classList.toggle("network-error-row", this._isFailed());
2521 WebInspector.SortableDataGridNode.prototype.createCells.call(this);
2523 this.refreshGraph(this._parentView.calculator());
2528 * @param {string} columnIdentifier
2529 * @return {!Element}
2531 createCell: function(columnIdentifier)
2533 var cell = this.createTD(columnIdentifier);
2534 switch (columnIdentifier) {
2535 case "name": this._renderNameCell(cell); break;
2536 case "timeline": this._createTimelineBar(cell); break;
2537 case "method": cell.setTextAndTitle(this._request.requestMethod); break;
2538 case "status": this._renderStatusCell(cell); break;
2539 case "scheme": cell.setTextAndTitle(this._request.scheme); break;
2540 case "domain": cell.setTextAndTitle(this._request.domain); break;
2541 case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
2542 case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
2543 case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
2544 case "type": this._renderTypeCell(cell); break;
2545 case "initiator": this._renderInitiatorCell(cell); break;
2546 case "size": this._renderSizeCell(cell); break;
2547 case "time": this._renderTimeCell(cell); break;
2548 default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
2555 * @param {?Array} array
2558 _arrayLength: function(array)
2560 return array ? "" + array.length : "";
2563 wasDetached: function()
2565 if (this._linkifiedInitiatorAnchor)
2566 this._linkifiedInitiatorAnchor.remove();
2569 _dispose: function()
2571 this._linkifier.reset();
2574 _onClick: function()
2576 if (!this._parentView._allowRequestSelection)
2582 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2583 WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
2585 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2586 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2587 url: this._request.url
2592 * @param {!RegExp=} regexp
2594 _highlightMatchedSubstring: function(regexp)
2596 // Ensure element is created.
2598 var domChanges = [];
2599 var matchInfo = this._nameCell.textContent.match(regexp);
2601 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2605 _openInNewTab: function()
2607 InspectorFrontendHost.openInNewTab(this._request.url);
2612 return this._parentView._allowRequestSelection;
2616 * @param {!Element} cell
2618 _createTimelineBar: function(cell)
2620 cell = cell.createChild("div");
2621 this._timelineCell = cell;
2623 cell.className = "network-graph-side";
2625 this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
2626 this._barAreaElement.request = this._request;
2628 var type = this._request.type.name();
2629 var cached = this._request.cached;
2631 this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
2632 this._barLeftElement.classList.add(type, "waiting");
2633 this._barLeftElement.classList.toggle("cached", cached);
2635 this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
2636 this._barRightElement.classList.add(type);
2637 this._barRightElement.classList.toggle("cached", cached);
2639 this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
2640 this._labelLeftElement.classList.add("waiting");
2642 this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
2644 cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2650 _isFailed: function()
2652 return !!this._request.failed || (this._request.statusCode >= 400);
2656 * @param {!Element} cell
2658 _renderNameCell: function(cell)
2660 this._nameCell = cell;
2661 cell.addEventListener("click", this._onClick.bind(this), false);
2662 cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2664 if (this._request.type === WebInspector.resourceTypes.Image) {
2665 var previewImage = document.createElementWithClass("img", "image-network-icon-preview");
2666 this._request.populateImageSource(previewImage);
2668 iconElement = document.createElementWithClass("div", "icon");
2669 iconElement.appendChild(previewImage);
2671 iconElement = document.createElementWithClass("img", "icon");
2673 iconElement.classList.add(this._request.type.name());
2675 cell.appendChild(iconElement);
2676 cell.appendChild(document.createTextNode(this._request.name()));
2677 this._appendSubtitle(cell, this._request.path());
2678 cell.title = this._request.url;
2682 * @param {!Element} cell
2684 _renderStatusCell: function(cell)
2686 cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2688 if (this._request.failed && !this._request.canceled) {
2689 var failText = WebInspector.UIString("(failed)");
2690 if (this._request.localizedFailDescription) {
2691 cell.appendChild(document.createTextNode(failText));
2692 this._appendSubtitle(cell, this._request.localizedFailDescription);
2693 cell.title = failText + " " + this._request.localizedFailDescription;
2695 cell.setTextAndTitle(failText);
2696 } else if (this._request.statusCode) {
2697 cell.appendChild(document.createTextNode("" + this._request.statusCode));
2698 this._appendSubtitle(cell, this._request.statusText);
2699 cell.title = this._request.statusCode + " " + this._request.statusText;
2700 } else if (this._request.parsedURL.isDataURL()) {
2701 cell.setTextAndTitle(WebInspector.UIString("(data)"));
2702 } else if (this._request.isPingRequest()) {
2703 cell.setTextAndTitle(WebInspector.UIString("(ping)"));
2704 } else if (this._request.canceled) {
2705 cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2706 } else if (this._request.finished) {
2707 cell.setTextAndTitle(WebInspector.UIString("Finished"));
2709 cell.setTextAndTitle(WebInspector.UIString("(pending)"));
2714 * @param {!Element} cell
2716 _renderTypeCell: function(cell)
2718 if (this._request.mimeType) {
2719 cell.setTextAndTitle(this._request.mimeType);
2721 cell.classList.toggle("network-dim-cell", !this._request.isPingRequest());
2722 cell.setTextAndTitle(this._request.requestContentType() || "");
2727 * @param {!Element} cell
2729 _renderInitiatorCell: function(cell)
2731 var request = this._request;
2732 var initiator = request.initiatorInfo();
2734 switch (initiator.type) {
2735 case WebInspector.NetworkRequest.InitiatorType.Parser:
2736 cell.title = initiator.url + ":" + initiator.lineNumber;
2737 cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2738 this._appendSubtitle(cell, WebInspector.UIString("Parser"));
2741 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2742 cell.title = initiator.url;
2743 console.assert(request.redirectSource);
2744 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2745 cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2746 this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
2749 case WebInspector.NetworkRequest.InitiatorType.Script:
2750 if (!this._linkifiedInitiatorAnchor) {
2751 this._linkifiedInitiatorAnchor = this._linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2752 this._linkifiedInitiatorAnchor.title = "";
2754 cell.appendChild(this._linkifiedInitiatorAnchor);
2755 this._appendSubtitle(cell, WebInspector.UIString("Script"));
2756 cell.classList.add("network-script-initiated");
2757 cell.request = request;
2762 cell.classList.add("network-dim-cell");
2763 cell.setTextAndTitle(WebInspector.UIString("Other"));
2768 * @param {!Element} cell
2770 _renderSizeCell: function(cell)
2772 if (this._request.cached) {
2773 cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2774 cell.classList.add("network-dim-cell");
2776 var resourceSize = Number.bytesToString(this._request.resourceSize);
2777 var transferSize = Number.bytesToString(this._request.transferSize);
2778 cell.setTextAndTitle(transferSize);
2779 this._appendSubtitle(cell, resourceSize);
2784 * @param {!Element} cell
2786 _renderTimeCell: function(cell)
2788 if (this._request.duration > 0) {
2789 cell.setTextAndTitle(Number.secondsToString(this._request.duration));
2790 this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
2792 cell.classList.add("network-dim-cell");
2793 cell.setTextAndTitle(WebInspector.UIString("Pending"));
2798 * @param {!Element} cellElement
2799 * @param {string} subtitleText
2801 _appendSubtitle: function(cellElement, subtitleText)
2803 var subtitleElement = document.createElement("div");
2804 subtitleElement.className = "network-cell-subtitle";
2805 subtitleElement.textContent = subtitleText;
2806 cellElement.appendChild(subtitleElement);
2810 * @param {!WebInspector.NetworkBaseCalculator} calculator
2812 refreshGraph: function(calculator)
2814 if (!this._timelineCell)
2817 var percentages = calculator.computeBarGraphPercentages(this._request);
2818 this._percentages = percentages;
2820 this._barAreaElement.classList.remove("hidden");
2822 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2823 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2825 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
2826 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2828 var labels = calculator.computeBarGraphLabels(this._request);
2829 this._labelLeftElement.textContent = labels.left;
2830 this._labelRightElement.textContent = labels.right;
2832 var tooltip = (labels.tooltip || "");
2833 this._barLeftElement.title = tooltip;
2834 this._labelLeftElement.title = tooltip;
2835 this._labelRightElement.title = tooltip;
2836 this._barRightElement.title = tooltip;
2839 _refreshLabelPositions: function()
2841 if (!this._percentages)
2843 this._labelLeftElement.style.removeProperty("left");
2844 this._labelLeftElement.style.removeProperty("right");
2845 this._labelLeftElement.classList.remove("before");
2846 this._labelLeftElement.classList.remove("hidden");
2848 this._labelRightElement.style.removeProperty("left");
2849 this._labelRightElement.style.removeProperty("right");
2850 this._labelRightElement.classList.remove("after");
2851 this._labelRightElement.classList.remove("hidden");
2853 const labelPadding = 10;
2854 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2855 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2857 if (this._barLeftElement) {
2858 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2859 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2861 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2862 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2865 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2866 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2868 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2869 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2870 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2872 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2873 var leftHidden = true;
2875 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2876 var rightHidden = true;
2878 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2879 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2880 if (labelBefore && !labelAfter)
2882 else if (labelAfter && !labelBefore)
2888 this._labelLeftElement.classList.add("hidden");
2889 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2890 this._labelLeftElement.classList.add("before");
2892 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2893 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2898 this._labelRightElement.classList.add("hidden");
2899 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2900 this._labelRightElement.classList.add("after");
2902 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2903 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2907 __proto__: WebInspector.SortableDataGridNode.prototype
2911 * @param {!WebInspector.NetworkDataGridNode} a
2912 * @param {!WebInspector.NetworkDataGridNode} b
2915 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2917 var aFileName = a._request.name();
2918 var bFileName = b._request.name();
2919 if (aFileName > bFileName)
2921 if (bFileName > aFileName)
2923 return a._request.indentityCompare(b._request);
2927 * @param {!WebInspector.NetworkDataGridNode} a
2928 * @param {!WebInspector.NetworkDataGridNode} b
2931 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2933 var aRemoteAddress = a._request.remoteAddress();
2934 var bRemoteAddress = b._request.remoteAddress();
2935 if (aRemoteAddress > bRemoteAddress)
2937 if (bRemoteAddress > aRemoteAddress)
2939 return a._request.indentityCompare(b._request);
2943 * @param {!WebInspector.NetworkDataGridNode} a
2944 * @param {!WebInspector.NetworkDataGridNode} b
2947 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2949 if (b._request.cached && !a._request.cached)
2951 if (a._request.cached && !b._request.cached)
2953 return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
2957 * @param {!WebInspector.NetworkDataGridNode} a
2958 * @param {!WebInspector.NetworkDataGridNode} b
2961 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2963 var aInitiator = a._request.initiatorInfo();
2964 var bInitiator = b._request.initiatorInfo();
2966 if (aInitiator.type < bInitiator.type)
2968 if (aInitiator.type > bInitiator.type)
2971 if (typeof aInitiator.__source === "undefined")
2972 aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
2973 if (typeof bInitiator.__source === "undefined")
2974 bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
2976 if (aInitiator.__source < bInitiator.__source)
2978 if (aInitiator.__source > bInitiator.__source)
2981 if (aInitiator.lineNumber < bInitiator.lineNumber)
2983 if (aInitiator.lineNumber > bInitiator.lineNumber)
2986 if (aInitiator.columnNumber < bInitiator.columnNumber)
2988 if (aInitiator.columnNumber > bInitiator.columnNumber)
2991 return a._request.indentityCompare(b._request);
2995 * @param {!WebInspector.NetworkDataGridNode} a
2996 * @param {!WebInspector.NetworkDataGridNode} b
2999 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
3001 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
3002 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
3003 return (aScore - bScore) || a._request.indentityCompare(b._request);
3007 * @param {!WebInspector.NetworkDataGridNode} a
3008 * @param {!WebInspector.NetworkDataGridNode} b
3011 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
3013 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
3014 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
3015 return (aScore - bScore) || a._request.indentityCompare(b._request);
3019 * @param {string} propertyName
3020 * @param {boolean} revert
3021 * @param {!WebInspector.NetworkDataGridNode} a
3022 * @param {!WebInspector.NetworkDataGridNode} b
3025 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
3027 var aValue = a._request[propertyName];
3028 var bValue = b._request[propertyName];
3029 if (aValue > bValue)
3030 return revert ? -1 : 1;
3031 if (bValue > aValue)
3032 return revert ? 1 : -1;
3033 return a._request.indentityCompare(b._request);