2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 2011 Google Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 importScript("RequestView.js");
32 importScript("NetworkItemView.js");
33 importScript("RequestCookiesView.js");
34 importScript("RequestHeadersView.js");
35 importScript("RequestHTMLView.js");
36 importScript("RequestJSONView.js");
37 importScript("RequestPreviewView.js");
38 importScript("RequestResponseView.js");
39 importScript("RequestTimingView.js");
40 importScript("ResourceWebSocketFrameView.js");
44 * @implements {WebInspector.Searchable}
45 * @extends {WebInspector.View}
46 * @param {!WebInspector.FilterBar} filterBar
47 * @param {!WebInspector.Setting} coulmnsVisibilitySetting
49 WebInspector.NetworkLogView = function(filterBar, coulmnsVisibilitySetting)
51 WebInspector.View.call(this);
52 this.registerRequiredCSS("networkLogView.css");
53 this.registerRequiredCSS("filter.css");
55 this._filterBar = filterBar;
56 this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
57 this._allowRequestSelection = false;
59 this._requestsById = {};
60 this._requestsByURL = {};
61 this._staleRequests = {};
62 this._requestGridNodes = {};
63 this._lastRequestGridNodeId = 0;
64 this._mainRequestLoadTime = -1;
65 this._mainRequestDOMContentLoadedTime = -1;
66 this._matchedRequests = [];
67 this._highlightedSubstringChanges = [];
68 this._filteredOutRequests = new Map();
70 this._matchedRequestsMap = {};
71 this._currentMatchedRequestIndex = -1;
73 this._createStatusbarButtons();
74 this._createStatusBarItems();
75 this._linkifier = new WebInspector.Linkifier();
77 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
78 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
79 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
81 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
82 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
83 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
84 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
87 this._initializeView();
88 this._recordButton.toggled = true;
89 WebInspector.networkLog.requests.forEach(this._appendRequest.bind(this));
92 WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
93 WebInspector.NetworkLogView._responseHeaderColumns = ["Cache-Control", "Connection", "Content-Encoding", "Content-Length", "ETag", "Keep-Alive", "Last-Modified", "Server", "Vary"];
94 WebInspector.NetworkLogView._defaultColumnsVisibility = {
95 method: true, status: true, scheme: false, domain: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true,
96 "Cache-Control": false, "Connection": false, "Content-Encoding": false, "Content-Length": false, "ETag": false, "Keep-Alive": false, "Last-Modified": false, "Server": false, "Vary": false
98 WebInspector.NetworkLogView._defaultRefreshDelay = 500;
100 WebInspector.NetworkLogView.prototype = {
101 _addFilters: function()
103 this._textFilterUI = new WebInspector.TextFilterUI();
104 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
105 this._filterBar.addFilter(this._textFilterUI);
107 this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI();
108 for (var typeId in WebInspector.resourceTypes) {
109 var resourceType = WebInspector.resourceTypes[typeId];
110 this._resourceTypeFilterUI.addBit(resourceType.name(), resourceType.categoryTitle());
112 this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
113 this._filterBar.addFilter(this._resourceTypeFilterUI);
115 var dataURLSetting = WebInspector.settings.networkHideDataURL;
116 this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
117 this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
118 this._filterBar.addFilter(this._dataURLFilterUI);
121 _filterChanged: function(event)
123 this._removeAllNodeHighlights();
124 this.searchCanceled();
125 this._filterRequests();
128 _initializeView: function()
130 this.element.id = "network-container";
132 this._createSortingFunctions();
134 this._createTimelineGrid();
135 this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
137 if (!this.useLargeRows)
138 this._setLargerRequests(this.useLargeRows);
140 this._allowPopover = true;
141 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
142 // Enable faster hint.
143 this._popoverHelper.setTimeout(100);
145 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
147 this.switchToDetailedView();
152 return [this._recordButton.element, this._clearButton.element, this._filterBar.filterButton().element, this._largerRequestsButton.element, this._preserveLogCheckbox.element, this._progressBarContainer];
157 return WebInspector.settings.resourcesLargeRows.get();
160 set allowPopover(flag)
162 this._allowPopover = flag;
166 * @return {!Array.<!Element>}
168 elementsToRestoreScrollPositionsFor: function()
170 if (!this._dataGrid) // Not initialized yet.
172 return [this._dataGrid.scrollContainer];
177 this._updateOffscreenRows();
180 _createTimelineGrid: function()
182 this._timelineGrid = new WebInspector.TimelineGrid();
183 this._timelineGrid.element.classList.add("network-timeline-grid");
184 this._dataGrid.element.appendChild(this._timelineGrid.element);
187 _createTable: function()
192 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
193 title: WebInspector.UIString("Name"),
201 title: WebInspector.UIString("Method"),
208 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
209 title: WebInspector.UIString("Status"),
216 title: WebInspector.UIString("Scheme"),
223 title: WebInspector.UIString("Domain"),
230 title: WebInspector.UIString("Type"),
237 title: WebInspector.UIString("Initiator"),
244 title: WebInspector.UIString("Cookies"),
247 align: WebInspector.DataGrid.Align.Right
252 title: WebInspector.UIString("Set-Cookies"),
255 align: WebInspector.DataGrid.Align.Right
260 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
261 title: WebInspector.UIString("Size"),
264 align: WebInspector.DataGrid.Align.Right
269 titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
270 title: WebInspector.UIString("Time"),
273 align: WebInspector.DataGrid.Align.Right
276 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
277 for (var i = 0; i < responseHeaderColumns.length; ++i) {
278 var headerName = responseHeaderColumns[i];
281 title: WebInspector.UIString(headerName),
284 if (headerName === "Content-Length")
285 descriptor.align = WebInspector.DataGrid.Align.Right;
286 columns.push(descriptor);
291 titleDOMFragment: document.createDocumentFragment(),
292 title: WebInspector.UIString("Timeline"),
295 sort: WebInspector.DataGrid.Order.Ascending
298 this._dataGrid = new WebInspector.DataGrid(columns);
299 this._dataGrid.setName("networkLog");
300 this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
301 this._dataGrid.element.classList.add("network-log-grid");
302 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
303 this._dataGrid.show(this.element);
305 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
306 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
307 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
308 this._dataGrid.scrollContainer.addEventListener("scroll", this._updateOffscreenRows.bind(this));
310 this._patchTimelineHeader();
313 _makeHeaderFragment: function(title, subtitle)
315 var fragment = document.createDocumentFragment();
316 fragment.createTextChild(title);
317 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
318 subtitleDiv.createTextChild(subtitle);
322 _patchTimelineHeader: function()
324 var timelineSorting = document.createElement("select");
326 var option = document.createElement("option");
327 option.value = "startTime";
328 option.label = WebInspector.UIString("Timeline");
329 timelineSorting.appendChild(option);
331 option = document.createElement("option");
332 option.value = "startTime";
333 option.label = WebInspector.UIString("Start Time");
334 timelineSorting.appendChild(option);
336 option = document.createElement("option");
337 option.value = "responseTime";
338 option.label = WebInspector.UIString("Response Time");
339 timelineSorting.appendChild(option);
341 option = document.createElement("option");
342 option.value = "endTime";
343 option.label = WebInspector.UIString("End Time");
344 timelineSorting.appendChild(option);
346 option = document.createElement("option");
347 option.value = "duration";
348 option.label = WebInspector.UIString("Duration");
349 timelineSorting.appendChild(option);
351 option = document.createElement("option");
352 option.value = "latency";
353 option.label = WebInspector.UIString("Latency");
354 timelineSorting.appendChild(option);
356 var header = this._dataGrid.headerTableHeader("timeline");
357 header.replaceChild(timelineSorting, header.firstChild);
359 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
360 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
361 this._timelineSortSelector = timelineSorting;
364 _createSortingFunctions: function()
366 this._sortingFunctions = {};
367 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
368 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
369 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
370 this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
371 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
372 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
373 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
374 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
375 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
376 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
377 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
378 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
379 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
380 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
381 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
382 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
383 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
385 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
386 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
388 this._calculators = {};
389 this._calculators.timeline = timeCalculator;
390 this._calculators.startTime = timeCalculator;
391 this._calculators.endTime = timeCalculator;
392 this._calculators.responseTime = timeCalculator;
393 this._calculators.duration = durationCalculator;
394 this._calculators.latency = durationCalculator;
397 _sortItems: function()
399 this._removeAllNodeHighlights();
400 var columnIdentifier = this._dataGrid.sortColumnIdentifier();
401 if (columnIdentifier === "timeline") {
402 this._sortByTimeline();
405 var sortingFunction = this._sortingFunctions[columnIdentifier];
406 if (!sortingFunction)
409 this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
410 this._timelineSortSelector.selectedIndex = 0;
411 this._updateOffscreenRows();
413 this.searchCanceled();
415 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
416 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
417 column: columnIdentifier,
418 sortOrder: this._dataGrid.sortOrder()
422 _sortByTimeline: function()
424 this._removeAllNodeHighlights();
425 var selectedIndex = this._timelineSortSelector.selectedIndex;
427 selectedIndex = 1; // Sort by start time by default.
428 var selectedOption = this._timelineSortSelector[selectedIndex];
429 var value = selectedOption.value;
431 var sortingFunction = this._sortingFunctions[value];
432 this._dataGrid.sortNodes(sortingFunction);
433 this.calculator = this._calculators[value];
434 if (this.calculator.startAtZero)
435 this._timelineGrid.hideEventDividers();
437 this._timelineGrid.showEventDividers();
438 this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
439 this._updateOffscreenRows();
442 _createStatusBarItems: function()
444 this._progressBarContainer = document.createElement("div");
445 this._progressBarContainer.className = "status-bar-item";
448 _updateSummaryBar: function()
450 var requestsNumber = this._requests.length;
452 if (!requestsNumber) {
453 if (this._summaryBarElement._isDisplayingWarning)
455 this._summaryBarElement._isDisplayingWarning = true;
456 this._summaryBarElement.removeChildren();
457 this._summaryBarElement.createChild("div", "warning-icon-small");
458 var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
459 this._summaryBarElement.appendChild(document.createTextNode(text));
460 this._summaryBarElement.title = text;
463 delete this._summaryBarElement._isDisplayingWarning;
465 var transferSize = 0;
466 var selectedRequestsNumber = 0;
467 var selectedTransferSize = 0;
470 for (var i = 0; i < this._requests.length; ++i) {
471 var request = this._requests[i];
472 var requestTransferSize = request.transferSize;
473 transferSize += requestTransferSize;
474 if (!this._filteredOutRequests.get(request)) {
475 selectedRequestsNumber++;
476 selectedTransferSize += requestTransferSize;
478 if (request.url === WebInspector.inspectedPageURL)
479 baseTime = request.startTime;
480 if (request.endTime > maxTime)
481 maxTime = request.endTime;
484 if (selectedRequestsNumber !== requestsNumber) {
485 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
486 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
488 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
489 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
491 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
492 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
493 Number.secondsToString(maxTime - baseTime),
494 Number.secondsToString(this._mainRequestLoadTime - baseTime),
495 Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
497 this._summaryBarElement.textContent = text;
498 this._summaryBarElement.title = text;
501 _scheduleRefresh: function()
503 if (this._needsRefresh)
506 this._needsRefresh = true;
508 if (this.isShowing() && !this._refreshTimeout)
509 this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
512 _updateDividersIfNeeded: function()
516 var timelineColumn = this._dataGrid.columns.timeline;
517 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
518 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnIndex) {
519 // Position timline grid location.
520 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
525 if (!this.isShowing()) {
526 this._scheduleRefresh();
529 this.calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
530 proceed = this._timelineGrid.updateDividers(this.calculator);
535 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
536 // If our current sorting method starts at zero, that means it shows all
537 // requests starting at the same point, and so onLoad event and DOMContent
538 // event lines really wouldn't make much sense here, so don't render them.
539 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
540 // function defined, we are probably sorting by size, and event times aren't relevant
545 this._timelineGrid.removeEventDividers();
546 if (this._mainRequestLoadTime !== -1) {
547 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
549 var loadDivider = document.createElement("div");
550 loadDivider.className = "network-event-divider network-red-divider";
552 var loadDividerPadding = document.createElement("div");
553 loadDividerPadding.className = "network-event-divider-padding";
554 loadDividerPadding.title = WebInspector.UIString("Load event fired");
555 loadDividerPadding.appendChild(loadDivider);
556 loadDividerPadding.style.left = percent + "%";
557 this._timelineGrid.addEventDivider(loadDividerPadding);
560 if (this._mainRequestDOMContentLoadedTime !== -1) {
561 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
563 var domContentLoadedDivider = document.createElement("div");
564 domContentLoadedDivider.className = "network-event-divider network-blue-divider";
566 var domContentLoadedDividerPadding = document.createElement("div");
567 domContentLoadedDividerPadding.className = "network-event-divider-padding";
568 domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event fired");
569 domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
570 domContentLoadedDividerPadding.style.left = percent + "%";
571 this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
575 _refreshIfNeeded: function()
577 if (this._needsRefresh)
581 _invalidateAllItems: function()
583 for (var i = 0; i < this._requests.length; ++i) {
584 var request = this._requests[i];
585 this._staleRequests[request.requestId] = request;
591 return this._calculator;
596 if (!x || this._calculator === x)
599 this._calculator = x;
600 this._calculator.reset();
602 this._invalidateAllItems();
606 _requestGridNode: function(request)
608 return this._requestGridNodes[request.__gridNodeId];
611 _createRequestGridNode: function(request)
613 var node = new WebInspector.NetworkDataGridNode(this, request);
614 request.__gridNodeId = this._lastRequestGridNodeId++;
615 this._requestGridNodes[request.__gridNodeId] = node;
619 _createStatusbarButtons: function()
621 this._recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record Network Log"), "record-profile-status-bar-item");
622 this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
624 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
625 this._clearButton.addEventListener("click", this._reset, this);
627 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
628 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
629 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
631 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
634 _loadEventFired: function(event)
636 if (!this._recordButton.toggled)
639 this._mainRequestLoadTime = event.data || -1;
640 // Schedule refresh to update boundaries and draw the new line.
641 this._scheduleRefresh();
644 _domContentLoadedEventFired: function(event)
646 if (!this._recordButton.toggled)
648 this._mainRequestDOMContentLoadedTime = event.data || -1;
649 // Schedule refresh to update boundaries and draw the new line.
650 this._scheduleRefresh();
655 this._refreshIfNeeded();
660 this._popoverHelper.hidePopover();
665 this._needsRefresh = false;
666 if (this._refreshTimeout) {
667 clearTimeout(this._refreshTimeout);
668 delete this._refreshTimeout;
671 this._removeAllNodeHighlights();
672 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
673 var boundariesChanged = false;
674 if (this.calculator.updateBoundariesForEventTime) {
675 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
676 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
679 for (var requestId in this._staleRequests) {
680 var request = this._staleRequests[requestId];
681 var node = this._requestGridNode(request);
683 // Create the timeline tree element and graph.
684 node = this._createRequestGridNode(request);
685 this._dataGrid.rootNode().appendChild(node);
687 node.refreshRequest();
688 this._applyFilter(node);
690 if (this.calculator.updateBoundaries(request))
691 boundariesChanged = true;
693 if (!node.isFilteredOut())
694 this._updateHighlightIfMatched(request);
697 if (boundariesChanged) {
698 // The boundaries changed, so all item graphs are stale.
699 this._invalidateAllItems();
702 for (var requestId in this._staleRequests)
703 this._requestGridNode(this._staleRequests[requestId]).refreshGraph(this.calculator);
705 this._staleRequests = {};
707 this._updateSummaryBar();
708 this._dataGrid.updateWidths();
709 // FIXME: evaluate performance impact of moving this before a call to sortItems()
710 if (wasScrolledToLastRow)
711 this._dataGrid.scrollToLastRow();
714 _onRecordButtonClicked: function(e)
716 if (!this._recordButton.toggled)
718 this._recordButton.toggled = !this._recordButton.toggled;
723 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
725 this._clearSearchMatchedList();
726 if (this._popoverHelper)
727 this._popoverHelper.hidePopover();
729 if (this._calculator)
730 this._calculator.reset();
733 this._requestsById = {};
734 this._requestsByURL = {};
735 this._staleRequests = {};
736 this._requestGridNodes = {};
738 if (this._dataGrid) {
739 this._dataGrid.rootNode().removeChildren();
740 this._updateDividersIfNeeded();
741 this._updateSummaryBar();
744 this._mainRequestLoadTime = -1;
745 this._mainRequestDOMContentLoadedTime = -1;
750 return this._requests;
754 * @return {!WebInspector.NetworkRequest}
756 requestById: function(id)
758 return this._requestsById[id];
761 _onRequestStarted: function(event)
763 if (this._recordButton.toggled)
764 this._appendRequest(event.data);
767 _appendRequest: function(request)
769 this._requests.push(request);
771 // In case of redirect request id is reassigned to a redirected
772 // request and we need to update _requestsById and search results.
773 if (this._requestsById[request.requestId]) {
774 var oldRequest = request.redirects[request.redirects.length - 1];
775 this._requestsById[oldRequest.requestId] = oldRequest;
777 this._updateSearchMatchedListAfterRequestIdChanged(request.requestId, oldRequest.requestId);
779 this._requestsById[request.requestId] = request;
781 this._requestsByURL[request.url] = request;
783 // Pull all the redirects of the main request upon commit load.
784 if (request.redirects) {
785 for (var i = 0; i < request.redirects.length; ++i)
786 this._refreshRequest(request.redirects[i]);
789 this._refreshRequest(request);
793 * @param {!WebInspector.Event} event
795 _onRequestUpdated: function(event)
797 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
798 this._refreshRequest(request);
802 * @param {!WebInspector.NetworkRequest} request
804 _refreshRequest: function(request)
806 if (!this._requestsById[request.requestId])
808 this._staleRequests[request.requestId] = request;
809 this._scheduleRefresh();
812 _willReloadPage: function(event)
814 this._recordButton.toggled = true;
815 if (!this._preserveLogCheckbox.checked())
820 * @param {!WebInspector.Event} event
822 _mainFrameNavigated: function(event)
824 if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
827 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
828 var loaderId = frame.loaderId;
830 // Pick provisional load requests.
831 var requestsToPick = [];
832 var requests = WebInspector.networkLog.requests;
833 for (var i = 0; i < requests.length; ++i) {
834 var request = requests[i];
835 if (request.loaderId === loaderId)
836 requestsToPick.push(request);
841 for (var i = 0; i < requestsToPick.length; ++i)
842 this._appendRequest(requestsToPick[i]);
845 switchToDetailedView: function()
849 if (this._dataGrid.selectedNode)
850 this._dataGrid.selectedNode.selected = false;
852 this.element.classList.remove("brief-mode");
853 this._detailedMode = true;
854 this._updateColumns();
857 switchToBriefView: function()
859 this.element.classList.add("brief-mode");
860 this._removeAllNodeHighlights();
861 this._detailedMode = false;
862 this._updateColumns();
863 this._popoverHelper.hidePopover();
866 _toggleLargerRequests: function()
868 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
869 this._setLargerRequests(WebInspector.settings.resourcesLargeRows.get());
872 _setLargerRequests: function(enabled)
874 this._largerRequestsButton.toggled = enabled;
876 this._largerRequestsButton.title = WebInspector.UIString("Use large resource rows.");
877 this._dataGrid.element.classList.add("small");
878 this._timelineGrid.element.classList.add("small");
880 this._largerRequestsButton.title = WebInspector.UIString("Use small resource rows.");
881 this._dataGrid.element.classList.remove("small");
882 this._timelineGrid.element.classList.remove("small");
884 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
885 this._updateOffscreenRows();
888 _getPopoverAnchor: function(element)
890 if (!this._allowPopover)
892 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
893 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
895 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
896 if (anchor && anchor.request && anchor.request.initiator)
903 * @param {!Element} anchor
904 * @param {!WebInspector.Popover} popover
906 _showPopover: function(anchor, popover)
909 if (anchor.classList.contains("network-script-initiated"))
910 content = this._generateScriptInitiatedPopoverContent(anchor.request);
912 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
913 popover.show(content, anchor);
916 _onHidePopover: function()
918 this._linkifier.reset();
922 * @param {!WebInspector.NetworkRequest} request
925 _generateScriptInitiatedPopoverContent: function(request)
927 var stackTrace = request.initiator.stackTrace;
928 var framesTable = document.createElement("table");
929 for (var i = 0; i < stackTrace.length; ++i) {
930 var stackFrame = stackTrace[i];
931 var row = document.createElement("tr");
932 row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
933 row.createChild("td").textContent = " @ ";
934 row.createChild("td").appendChild(this._linkifier.linkifyLocation(stackFrame.url, stackFrame.lineNumber - 1, stackFrame.columnNumber - 1));
935 framesTable.appendChild(row);
940 _updateColumns: function()
942 var columnsVisibility = this._coulmnsVisibilitySetting.get();
943 var detailedMode = !!this._detailedMode;
944 for (var columnIdentifier in columnsVisibility) {
945 var visible = detailedMode && columnsVisibility[columnIdentifier];
946 this._dataGrid.setColumnVisible(columnIdentifier, visible);
948 this._dataGrid.setColumnVisible("timeline", detailedMode);
949 this._dataGrid.applyColumnWeights();
953 * @param {string} columnIdentifier
955 _toggleColumnVisibility: function(columnIdentifier)
957 var columnsVisibility = this._coulmnsVisibilitySetting.get();
958 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
959 this._coulmnsVisibilitySetting.set(columnsVisibility);
961 this._updateColumns();
965 * @return {!Array.<string>}
967 _getConfigurableColumnIDs: function()
969 if (this._configurableColumnIDs)
970 return this._configurableColumnIDs;
972 var columns = this._dataGrid.columns;
973 function compare(id1, id2)
975 return columns[id1].title.compareTo(columns[id2].title);
978 var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
979 this._configurableColumnIDs = columnIDs.sort(compare);
980 return this._configurableColumnIDs;
983 _contextMenu: function(event)
985 var contextMenu = new WebInspector.ContextMenu(event);
987 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
988 var columnsVisibility = this._coulmnsVisibilitySetting.get();
989 var columnIDs = this._getConfigurableColumnIDs();
990 for (var i = 0; i < columnIDs.length; ++i) {
991 var columnIdentifier = columnIDs[i];
992 var column = this._dataGrid.columns[columnIdentifier];
993 contextMenu.appendCheckboxItem(column.title, this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
999 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1000 var request = gridNode && gridNode._request;
1003 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, request.url, false));
1004 contextMenu.appendSeparator();
1005 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1006 if (request.requestHeadersText())
1007 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1008 if (request.responseHeadersText)
1009 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1010 contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1012 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1014 contextMenu.appendSeparator();
1015 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1017 contextMenu.appendSeparator();
1018 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1019 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1021 if (request && request.type === WebInspector.resourceTypes.XHR) {
1022 contextMenu.appendSeparator();
1023 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
1024 contextMenu.appendSeparator();
1030 _replayXHR: function(requestId)
1032 NetworkAgent.replayXHR(requestId);
1035 _copyAll: function()
1038 log: (new WebInspector.HARLog(this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter))).build()
1040 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1043 _copyLocation: function(request)
1045 InspectorFrontendHost.copyText(request.url);
1048 _copyRequestHeaders: function(request)
1050 InspectorFrontendHost.copyText(request.requestHeadersText());
1053 _copyResponseHeaders: function(request)
1055 InspectorFrontendHost.copyText(request.responseHeadersText);
1059 * @param {!WebInspector.NetworkRequest} request
1061 _copyCurlCommand: function(request)
1063 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1066 _exportAll: function()
1068 var filename = WebInspector.inspectedPageDomain + ".har";
1069 var stream = new WebInspector.FileOutputStream();
1070 stream.open(filename, openCallback.bind(this));
1073 * @param {boolean} accepted
1074 * @this {WebInspector.NetworkLogView}
1076 function openCallback(accepted)
1080 var progressIndicator = new WebInspector.ProgressIndicator();
1081 this._progressBarContainer.appendChild(progressIndicator.element);
1082 var harWriter = new WebInspector.HARWriter();
1083 harWriter.write(stream, this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter), progressIndicator);
1087 _clearBrowserCache: function()
1089 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1090 NetworkAgent.clearBrowserCache();
1093 _clearBrowserCookies: function()
1095 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1096 NetworkAgent.clearBrowserCookies();
1099 _updateOffscreenRows: function()
1101 var dataTableBody = this._dataGrid.dataTableBody;
1102 var rows = dataTableBody.children;
1103 var recordsCount = rows.length;
1104 if (recordsCount < 2)
1105 return; // Filler row only.
1107 var visibleTop = this._dataGrid.scrollContainer.scrollTop;
1108 var visibleBottom = visibleTop + this._dataGrid.scrollContainer.offsetHeight;
1112 // Filler is at recordsCount - 1.
1113 var unfilteredRowIndex = 0;
1114 for (var i = 0; i < recordsCount - 1; ++i) {
1117 var dataGridNode = this._dataGrid.dataGridNodeFromNode(row);
1118 if (dataGridNode.isFilteredOut()) {
1119 row.classList.remove("offscreen");
1124 rowHeight = row.offsetHeight;
1126 var rowIsVisible = unfilteredRowIndex * rowHeight < visibleBottom && (unfilteredRowIndex + 1) * rowHeight > visibleTop;
1127 if (rowIsVisible !== row.rowIsVisible) {
1128 row.enableStyleClass("offscreen", !rowIsVisible);
1129 row.rowIsVisible = rowIsVisible;
1131 var rowIsOdd = !!(unfilteredRowIndex & 1);
1132 if (rowIsOdd !== row.rowIsOdd) {
1133 row.enableStyleClass("odd", rowIsOdd);
1134 row.rowIsOdd = rowIsOdd;
1136 unfilteredRowIndex++;
1140 _matchRequest: function(request)
1142 if (!this._searchRegExp)
1145 if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp))
1148 if (request.requestId in this._matchedRequestsMap)
1149 return this._matchedRequestsMap[request.requestId];
1151 var matchedRequestIndex = this._matchedRequests.length;
1152 this._matchedRequestsMap[request.requestId] = matchedRequestIndex;
1153 this._matchedRequests.push(request.requestId);
1155 return matchedRequestIndex;
1158 _clearSearchMatchedList: function()
1160 delete this._searchRegExp;
1161 this._matchedRequests = [];
1162 this._matchedRequestsMap = {};
1163 this._removeAllHighlights();
1166 _updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
1168 var requestIndex = this._matchedRequestsMap[oldRequestId];
1170 delete this._matchedRequestsMap[oldRequestId];
1171 this._matchedRequestsMap[newRequestId] = requestIndex;
1172 this._matchedRequests[requestIndex] = newRequestId;
1176 _updateHighlightIfMatched: function(request)
1178 var matchedRequestIndex = this._matchRequest(request);
1179 if (matchedRequestIndex === -1)
1182 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1184 if (this._currentMatchedRequestIndex !== -1 && this._currentMatchedRequestIndex !== matchedRequestIndex)
1187 this._highlightNthMatchedRequestForSearch(matchedRequestIndex, false);
1190 _removeAllHighlights: function()
1192 this._removeAllNodeHighlights();
1193 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1194 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1195 this._highlightedSubstringChanges = [];
1199 * @param {!WebInspector.NetworkRequest} request
1200 * @param {boolean} reveal
1201 * @param {!RegExp=} regExp
1203 _highlightMatchedRequest: function(request, reveal, regExp)
1205 var node = this._requestGridNode(request);
1209 var nameMatched = request.name().match(regExp);
1210 var pathMatched = request.path().match(regExp);
1211 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1212 this._toggleLargerRequests();
1213 var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
1214 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1217 this._highlightNode(node);
1222 * @param {number} matchedRequestIndex
1223 * @param {boolean} reveal
1225 _highlightNthMatchedRequestForSearch: function(matchedRequestIndex, reveal)
1227 var request = this.requestById(this._matchedRequests[matchedRequestIndex]);
1230 this._removeAllHighlights();
1231 this._highlightMatchedRequest(request, reveal, this._searchRegExp);
1232 var node = this._requestGridNode(request);
1234 this._currentMatchedRequestIndex = matchedRequestIndex;
1236 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedRequestIndex);
1240 * @param {string} query
1241 * @param {boolean} shouldJump
1243 performSearch: function(query, shouldJump)
1245 var newMatchedRequestIndex = 0;
1246 var currentMatchedRequestId;
1247 if (this._currentMatchedRequestIndex !== -1)
1248 currentMatchedRequestId = this._matchedRequests[this._currentMatchedRequestIndex];
1250 this._clearSearchMatchedList();
1251 this._searchRegExp = createPlainTextSearchRegex(query, "i");
1253 var childNodes = this._dataGrid.dataTableBody.childNodes;
1254 var requestNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
1256 for (var i = 0; i < requestNodes.length; ++i) {
1257 var dataGridNode = this._dataGrid.dataGridNodeFromNode(requestNodes[i]);
1258 if (dataGridNode.isFilteredOut())
1260 if (this._matchRequest(dataGridNode._request) !== -1 && dataGridNode._request.requestId === currentMatchedRequestId)
1261 newMatchedRequestIndex = this._matchedRequests.length - 1;
1264 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1266 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, true);
1270 * @param {!WebInspector.NetworkDataGridNode} node
1272 _applyFilter: function(node)
1274 var filter = this._textFilterUI.regex();
1275 var request = node._request;
1278 if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1280 if (matches && !this._resourceTypeFilterUI.accept(request.type.name()))
1283 if (matches && filter) {
1284 matches = filter.test(request.name()) || filter.test(request.path());
1286 this._highlightMatchedRequest(request, false, filter);
1289 node.element.enableStyleClass("filtered-out", !matches);
1291 this._filteredOutRequests.remove(request);
1293 this._filteredOutRequests.put(request, true);
1296 _filterRequests: function()
1298 this._removeAllHighlights();
1299 this._filteredOutRequests.clear();
1301 var nodes = this._dataGrid.rootNode().children;
1302 for (var i = 0; i < nodes.length; ++i)
1303 this._applyFilter(nodes[i]);
1304 this._updateSummaryBar();
1305 this._updateOffscreenRows();
1308 jumpToPreviousSearchResult: function()
1310 if (!this._matchedRequests.length)
1312 this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + this._matchedRequests.length - 1) % this._matchedRequests.length, true);
1315 jumpToNextSearchResult: function()
1317 if (!this._matchedRequests.length)
1319 this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + 1) % this._matchedRequests.length, true);
1322 searchCanceled: function()
1324 this._clearSearchMatchedList();
1325 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1328 revealAndHighlightRequest: function(request)
1330 this._removeAllNodeHighlights();
1332 var node = this._requestGridNode(request);
1334 this._dataGrid.element.focus();
1336 this._highlightNode(node);
1340 _removeAllNodeHighlights: function()
1342 if (this._highlightedNode) {
1343 this._highlightedNode.element.classList.remove("highlighted-row");
1344 delete this._highlightedNode;
1348 _highlightNode: function(node)
1350 node.element.classList.add("highlighted-row");
1351 this._highlightedNode = node;
1355 * @param {!WebInspector.NetworkRequest} request
1358 _generateCurlCommand: function(request)
1360 var command = ["curl"];
1361 // These headers are derived from URL (except "version") and would be added by cURL anyway.
1362 var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1364 function escapeStringWin(str)
1366 /* Replace quote by double quote (but not by \") because it is
1367 recognized by both cmd.exe and MS Crt arguments parser.
1369 Replace % by "%" because it could be expanded to an environment
1370 variable value. So %% becomes "%""%". Even if an env variable ""
1371 (2 doublequotes) is declared, the cmd.exe will not
1372 substitute it with its value.
1374 Replace each backslash with double backslash to make sure
1375 MS Crt arguments parser won't collapse them.
1377 Replace new line outside of quotes since cmd.exe doesn't let
1380 return "\"" + str.replace(/"/g, "\"\"")
1381 .replace(/%/g, "\"%\"")
1382 .replace(/\\/g, "\\\\")
1383 .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1386 function escapeStringPosix(str)
1388 function escapeCharacter(x)
1390 var code = x.charCodeAt(0);
1392 // Add leading zero when needed to not care about the next character.
1393 return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1395 code = code.toString(16);
1396 return "\\u" + ("0000" + code).substr(code.length, 4);
1399 if (/[^\x20-\x7E]|\'/.test(str)) {
1400 // Use ANSI-C quoting syntax.
1401 return "$\'" + str.replace(/\\/g, "\\\\")
1402 .replace(/\'/g, "\\\'")
1403 .replace(/\n/g, "\\n")
1404 .replace(/\r/g, "\\r")
1405 .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1407 // Use single quote syntax.
1408 return "'" + str + "'";
1412 // cURL command expected to run on the same platform that DevTools run
1413 // (it may be different from the inspected page platform).
1414 var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1416 command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1418 var inferredMethod = "GET";
1420 var requestContentType = request.requestContentType();
1421 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1422 data.push("--data");
1423 data.push(escapeString(request.requestFormData));
1424 ignoredHeaders["content-length"] = true;
1425 inferredMethod = "POST";
1426 } else if (request.requestFormData) {
1427 data.push("--data-binary");
1428 data.push(escapeString(request.requestFormData));
1429 ignoredHeaders["content-length"] = true;
1430 inferredMethod = "POST";
1433 if (request.requestMethod !== inferredMethod) {
1435 command.push(request.requestMethod);
1438 var requestHeaders = request.requestHeaders();
1439 for (var i = 0; i < requestHeaders.length; i++) {
1440 var header = requestHeaders[i];
1441 var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1442 if (name.toLowerCase() in ignoredHeaders)
1445 command.push(escapeString(name + ": " + header.value));
1447 command = command.concat(data);
1448 command.push("--compressed");
1449 return command.join(" ");
1452 __proto__: WebInspector.View.prototype
1456 * @param {!WebInspector.NetworkRequest} request
1459 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1461 return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1465 WebInspector.NetworkLogView.EventTypes = {
1466 ViewCleared: "ViewCleared",
1467 RowSizeChanged: "RowSizeChanged",
1468 RequestSelected: "RequestSelected",
1469 SearchCountUpdated: "SearchCountUpdated",
1470 SearchIndexUpdated: "SearchIndexUpdated"
1475 * @implements {WebInspector.ContextMenu.Provider}
1476 * @implements {WebInspector.Searchable}
1477 * @extends {WebInspector.Panel}
1479 WebInspector.NetworkPanel = function()
1481 WebInspector.Panel.call(this, "network");
1482 this.registerRequiredCSS("networkPanel.css");
1483 this._injectStyles();
1485 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1486 this._filterBar = new WebInspector.FilterBar();
1487 this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1488 this._filtersContainer.appendChild(this._filterBar.filtersElement());
1489 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1491 this._searchableView = new WebInspector.SearchableView(this);
1492 this._searchableView.show(this.element);
1493 this._contentsElement = this._searchableView.element;
1495 this.createSidebarView(this._contentsElement);
1496 this.splitView.hideMainElement();
1498 var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisibility;
1499 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1500 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1501 var columnsVisibility = {};
1502 for (var columnId in defaultColumnsVisibility)
1503 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1504 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1506 this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1507 this.splitView.setSidebarView(this._networkLogView);
1509 this._viewsContainerElement = this.splitView.mainElement();
1510 this._viewsContainerElement.id = "network-views";
1511 this._viewsContainerElement.classList.add("hidden");
1512 if (!this._networkLogView.useLargeRows)
1513 this._viewsContainerElement.classList.add("small");
1515 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1516 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1517 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1518 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1519 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1521 this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1522 this._closeButtonElement.id = "network-close-button";
1523 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1524 this._viewsContainerElement.appendChild(this._closeButtonElement);
1526 for (var i = 0; i < this._networkLogView.statusBarItems.length; ++i)
1527 this._panelStatusBarElement.appendChild(this._networkLogView.statusBarItems[i]);
1530 * @this {WebInspector.NetworkPanel}
1532 function viewGetter()
1534 return this.visibleView;
1536 WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
1539 WebInspector.NetworkPanel.prototype = {
1540 _onFiltersToggled: function(event)
1542 var toggled = /** @type {boolean} */ (event.data);
1543 this._filtersContainer.enableStyleClass("hidden", !toggled);
1544 this.element.enableStyleClass("filters-toggled", toggled);
1548 * @return {!Array.<!Element>}
1550 elementsToRestoreScrollPositionsFor: function()
1552 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1556 * @return {!WebInspector.SearchableView}
1558 searchableView: function()
1560 return this._searchableView;
1563 // FIXME: only used by the layout tests, should not be exposed.
1566 this._networkLogView._reset();
1569 handleShortcut: function(event)
1571 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1572 this._toggleGridMode();
1573 event.handled = true;
1577 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1580 wasShown: function()
1582 WebInspector.Panel.prototype.wasShown.call(this);
1587 return this._networkLogView.requests;
1591 * @return {!WebInspector.NetworkRequest}
1593 requestById: function(id)
1595 return this._networkLogView.requestById(id);
1598 _requestByAnchor: function(anchor)
1600 return anchor.requestId ? this.requestById(anchor.requestId) : this._networkLogView._requestsByURL[anchor.href];
1604 * @param {!Element} anchor
1607 showAnchorLocation: function(anchor)
1609 var request = this._requestByAnchor(anchor);
1612 this.revealAndHighlightRequest(request)
1613 WebInspector.inspectorView.setCurrentPanel(this);
1617 revealAndHighlightRequest: function(request)
1619 this._toggleGridMode();
1621 this._networkLogView.revealAndHighlightRequest(request);
1624 _onViewCleared: function(event)
1626 this._closeVisibleRequest();
1627 this._toggleGridMode();
1628 this._viewsContainerElement.removeChildren();
1629 this._viewsContainerElement.appendChild(this._closeButtonElement);
1632 _onRowSizeChanged: function(event)
1634 this._viewsContainerElement.enableStyleClass("small", !event.data.largeRows);
1637 _onSearchCountUpdated: function(event)
1639 this._searchableView.updateSearchMatchesCount(event.data);
1642 _onSearchIndexUpdated: function(event)
1644 this._searchableView.updateCurrentMatchIndex(event.data);
1647 _onRequestSelected: function(event)
1649 this._showRequest(event.data);
1652 _showRequest: function(request)
1657 this._toggleViewingRequestMode();
1659 if (this.visibleView) {
1660 this.visibleView.detach();
1661 delete this.visibleView;
1664 var view = new WebInspector.NetworkItemView(request);
1665 view.show(this._viewsContainerElement);
1666 this.visibleView = view;
1669 _closeVisibleRequest: function()
1671 this.element.classList.remove("viewing-resource");
1673 if (this.visibleView) {
1674 this.visibleView.detach();
1675 delete this.visibleView;
1679 _toggleGridMode: function()
1681 if (this._viewingRequestMode) {
1682 this._viewingRequestMode = false;
1683 this.element.classList.remove("viewing-resource");
1684 this.splitView.hideMainElement();
1687 this._networkLogView.switchToDetailedView();
1688 this._networkLogView.allowPopover = true;
1689 this._networkLogView._allowRequestSelection = false;
1692 _toggleViewingRequestMode: function()
1694 if (this._viewingRequestMode)
1696 this._viewingRequestMode = true;
1698 this.element.classList.add("viewing-resource");
1699 this.splitView.showMainElement();
1700 this._networkLogView.allowPopover = false;
1701 this._networkLogView._allowRequestSelection = true;
1702 this._networkLogView.switchToBriefView();
1706 * @param {string} query
1707 * @param {boolean} shouldJump
1709 performSearch: function(query, shouldJump)
1711 this._networkLogView.performSearch(query, shouldJump);
1714 jumpToPreviousSearchResult: function()
1716 this._networkLogView.jumpToPreviousSearchResult();
1719 jumpToNextSearchResult: function()
1721 this._networkLogView.jumpToNextSearchResult();
1724 searchCanceled: function()
1726 this._networkLogView.searchCanceled();
1730 * @param {!WebInspector.ContextMenu} contextMenu
1731 * @param {!Object} target
1732 * @this {WebInspector.NetworkPanel}
1734 appendApplicableItems: function(event, contextMenu, target)
1737 * @this {WebInspector.NetworkPanel}
1739 function reveal(request)
1741 WebInspector.inspectorView.setCurrentPanel(this);
1742 this.revealAndHighlightRequest(request);
1746 * @this {WebInspector.NetworkPanel}
1748 function appendRevealItem(request)
1750 var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
1751 contextMenu.appendItem(revealText, reveal.bind(this, request));
1754 if (target instanceof WebInspector.Resource) {
1755 var resource = /** @type {!WebInspector.Resource} */ (target);
1756 if (resource.request)
1757 appendRevealItem.call(this, resource.request);
1760 if (target instanceof WebInspector.UISourceCode) {
1761 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
1762 var resource = WebInspector.resourceForURL(uiSourceCode.url);
1763 if (resource && resource.request)
1764 appendRevealItem.call(this, resource.request);
1768 if (!(target instanceof WebInspector.NetworkRequest))
1770 var request = /** @type {!WebInspector.NetworkRequest} */ (target);
1771 if (this.visibleView && this.visibleView.isShowing() && this.visibleView.request() === request)
1774 appendRevealItem.call(this, request);
1777 _injectStyles: function()
1779 var style = document.createElement("style");
1782 var columns = WebInspector.NetworkLogView._defaultColumnsVisibility;
1784 var hideSelectors = [];
1785 var bgSelectors = [];
1786 for (var columnId in columns) {
1787 hideSelectors.push("#network-container .hide-" + columnId + "-column ." + columnId + "-column");
1788 bgSelectors.push(".network-log-grid.data-grid td." + columnId + "-column");
1790 rules.push(hideSelectors.join(", ") + "{border-left: 0 none transparent;}");
1791 rules.push(bgSelectors.join(", ") + "{background-color: rgba(0, 0, 0, 0.07);}");
1793 style.textContent = rules.join("\n");
1794 document.head.appendChild(style);
1797 __proto__: WebInspector.Panel.prototype
1802 * @implements {WebInspector.ContextMenu.Provider}
1804 WebInspector.NetworkPanel.ContextMenuProvider = function()
1808 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
1810 * @param {!WebInspector.ContextMenu} contextMenu
1811 * @param {!Object} target
1813 appendApplicableItems: function(event, contextMenu, target)
1815 WebInspector.panel("network").appendApplicableItems(event, contextMenu, target);
1821 * @implements {WebInspector.TimelineGrid.Calculator}
1823 WebInspector.NetworkBaseCalculator = function()
1827 WebInspector.NetworkBaseCalculator.prototype = {
1829 * @param {number} time
1832 computePosition: function(time)
1834 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
1838 * @return {!{start: number, middle: number, end: number}}
1840 computeBarGraphPercentages: function(item)
1842 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
1846 * @return {!{left: string, right: string, tooltip: string}}
1848 computeBarGraphLabels: function(item)
1850 const label = this.formatTime(this._value(item));
1851 return {left: label, right: label, tooltip: label};
1857 boundarySpan: function()
1859 return this._maximumBoundary - this._minimumBoundary;
1865 updateBoundaries: function(item)
1867 this._minimumBoundary = 0;
1869 var value = this._value(item);
1870 if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
1871 this._maximumBoundary = value;
1879 delete this._minimumBoundary;
1880 delete this._maximumBoundary;
1886 maximumBoundary: function()
1888 return this._maximumBoundary;
1894 minimumBoundary: function()
1896 return this._minimumBoundary;
1902 zeroTime: function()
1904 return this._minimumBoundary;
1910 _value: function(item)
1916 * @param {number} value
1917 * @param {boolean=} hires
1920 formatTime: function(value, hires)
1922 return value.toString();
1925 setDisplayWindow: function(clientWidth)
1927 this._workingArea = clientWidth;
1928 this.paddingLeft = 0;
1934 * @extends {WebInspector.NetworkBaseCalculator}
1936 WebInspector.NetworkTimeCalculator = function(startAtZero)
1938 WebInspector.NetworkBaseCalculator.call(this);
1939 this.startAtZero = startAtZero;
1942 WebInspector.NetworkTimeCalculator.prototype = {
1944 * @param {!WebInspector.NetworkRequest} request
1945 * @return {!{start: number, middle: number, end: number}}
1947 computeBarGraphPercentages: function(request)
1949 if (request.startTime !== -1)
1950 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1954 if (request.responseReceivedTime !== -1)
1955 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1957 var middle = (this.startAtZero ? start : 100);
1959 if (request.endTime !== -1)
1960 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1962 var end = (this.startAtZero ? middle : 100);
1964 if (this.startAtZero) {
1970 return {start: start, middle: middle, end: end};
1976 computePercentageFromEventTime: function(eventTime)
1978 // This function computes a percentage in terms of the total loading time
1979 // of a specific event. If startAtZero is set, then this is useless, and we
1980 // want to return 0.
1981 if (eventTime !== -1 && !this.startAtZero)
1982 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1990 updateBoundariesForEventTime: function(eventTime)
1992 if (eventTime === -1 || this.startAtZero)
1995 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
1996 this._maximumBoundary = eventTime;
2003 * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2005 computeBarGraphLabels: function(request)
2007 var rightLabel = "";
2008 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2009 rightLabel = this.formatTime(request.endTime - request.responseReceivedTime);
2011 var hasLatency = request.latency > 0;
2013 var leftLabel = this.formatTime(request.latency);
2015 var leftLabel = rightLabel;
2018 return {left: leftLabel, right: rightLabel};
2020 if (hasLatency && rightLabel) {
2021 var total = this.formatTime(request.duration);
2022 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
2023 } else if (hasLatency)
2024 var tooltip = WebInspector.UIString("%s latency", leftLabel);
2025 else if (rightLabel)
2026 var tooltip = WebInspector.UIString("%s download", rightLabel);
2029 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
2030 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2036 updateBoundaries: function(request)
2038 var didChange = false;
2041 if (this.startAtZero)
2044 lowerBound = this._lowerBound(request);
2046 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2047 this._minimumBoundary = lowerBound;
2051 var upperBound = this._upperBound(request);
2052 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2053 this._maximumBoundary = upperBound;
2063 formatTime: function(value)
2065 return Number.secondsToString(value);
2068 _lowerBound: function(request)
2073 _upperBound: function(request)
2078 __proto__: WebInspector.NetworkBaseCalculator.prototype
2083 * @extends {WebInspector.NetworkTimeCalculator}
2085 WebInspector.NetworkTransferTimeCalculator = function()
2087 WebInspector.NetworkTimeCalculator.call(this, false);
2090 WebInspector.NetworkTransferTimeCalculator.prototype = {
2092 * @param {number} value
2095 formatTime: function(value)
2097 return Number.secondsToString(value);
2100 _lowerBound: function(request)
2102 return request.startTime;
2105 _upperBound: function(request)
2107 return request.endTime;
2110 __proto__: WebInspector.NetworkTimeCalculator.prototype
2115 * @extends {WebInspector.NetworkTimeCalculator}
2117 WebInspector.NetworkTransferDurationCalculator = function()
2119 WebInspector.NetworkTimeCalculator.call(this, true);
2122 WebInspector.NetworkTransferDurationCalculator.prototype = {
2124 * @param {number} value
2127 formatTime: function(value)
2129 return Number.secondsToString(value);
2132 _upperBound: function(request)
2134 return request.duration;
2137 __proto__: WebInspector.NetworkTimeCalculator.prototype
2142 * @extends {WebInspector.DataGridNode}
2143 * @param {!WebInspector.NetworkLogView} parentView
2144 * @param {!WebInspector.NetworkRequest} request
2146 WebInspector.NetworkDataGridNode = function(parentView, request)
2148 WebInspector.DataGridNode.call(this, {});
2149 this._parentView = parentView;
2150 this._request = request;
2151 this._linkifier = new WebInspector.Linkifier();
2154 WebInspector.NetworkDataGridNode.prototype = {
2156 createCells: function()
2158 // Out of sight, out of mind: create nodes offscreen to save on render tree update times when running updateOffscreenRows()
2159 this._element.classList.add("offscreen");
2160 this._nameCell = this._createDivInTD("name");
2161 this._methodCell = this._createDivInTD("method");
2162 this._statusCell = this._createDivInTD("status");
2163 this._schemeCell = this._createDivInTD("scheme");
2164 this._domainCell = this._createDivInTD("domain");
2165 this._typeCell = this._createDivInTD("type");
2166 this._initiatorCell = this._createDivInTD("initiator");
2167 this._cookiesCell = this._createDivInTD("cookies");
2168 this._setCookiesCell = this._createDivInTD("setCookies");
2169 this._sizeCell = this._createDivInTD("size");
2170 this._timeCell = this._createDivInTD("time");
2172 this._responseHeaderCells = {};
2173 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2174 for (var i = 0; i < responseHeaderColumns.length; ++i)
2175 this._responseHeaderCells[responseHeaderColumns[i]] = this._createDivInTD(responseHeaderColumns[i]);
2177 this._timelineCell = this._createDivInTD("timeline");
2178 this._createTimelineBar(this._timelineCell);
2179 this._nameCell.addEventListener("click", this._onClick.bind(this), false);
2180 this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2183 wasDetached: function()
2185 this._linkifier.reset();
2191 isFilteredOut: function()
2193 return !!this._parentView._filteredOutRequests.get(this._request);
2196 _onClick: function()
2198 if (!this._parentView._allowRequestSelection)
2204 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2205 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
2207 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2208 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2209 url: this._request.url
2213 _highlightMatchedSubstring: function(regexp)
2215 var domChanges = [];
2216 var matchInfo = this._element.textContent.match(regexp);
2218 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2222 _openInNewTab: function()
2224 InspectorFrontendHost.openInNewTab(this._request.url);
2229 return this._parentView._allowRequestSelection && !this.isFilteredOut();
2232 _createDivInTD: function(columnIdentifier)
2234 var td = this.createTD(columnIdentifier);
2235 var div = td.createChild("div");
2236 this._element.appendChild(td);
2241 * @param {!Element} cell
2243 _createTimelineBar: function(cell)
2245 cell.className = "network-graph-side";
2247 this._barAreaElement = document.createElement("div");
2248 // this._barAreaElement.className = "network-graph-bar-area hidden";
2249 this._barAreaElement.className = "network-graph-bar-area";
2250 this._barAreaElement.request = this._request;
2251 cell.appendChild(this._barAreaElement);
2253 this._barLeftElement = document.createElement("div");
2254 this._barLeftElement.className = "network-graph-bar waiting";
2255 this._barAreaElement.appendChild(this._barLeftElement);
2257 this._barRightElement = document.createElement("div");
2258 this._barRightElement.className = "network-graph-bar";
2259 this._barAreaElement.appendChild(this._barRightElement);
2262 this._labelLeftElement = document.createElement("div");
2263 this._labelLeftElement.className = "network-graph-label waiting";
2264 this._barAreaElement.appendChild(this._labelLeftElement);
2266 this._labelRightElement = document.createElement("div");
2267 this._labelRightElement.className = "network-graph-label";
2268 this._barAreaElement.appendChild(this._labelRightElement);
2270 cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2273 refreshRequest: function()
2275 this._refreshNameCell();
2276 this._refreshMethodCell();
2277 this._refreshStatusCell();
2278 this._refreshSchemeCell();
2279 this._refreshDomainCell();
2280 this._refreshTypeCell();
2281 this._refreshInitiatorCell();
2282 this._refreshCookiesCell();
2283 this._refreshSetCookiesCell();
2284 this._refreshSizeCell();
2285 this._refreshTimeCell();
2287 var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2288 for (var i = 0; i < responseHeaderColumns.length; ++i)
2289 this._refreshResponseHeaderCell(responseHeaderColumns[i]);
2291 if (this._request.cached)
2292 this._timelineCell.classList.add("resource-cached");
2294 this._element.classList.add("network-item");
2295 this._element.enableStyleClass("network-error-row", this._request.failed || (this._request.statusCode >= 400));
2296 this._updateElementStyleClasses(this._element);
2300 * @param {!Element} element
2302 _updateElementStyleClasses: function(element)
2304 var typeClassName = "network-type-" + this._request.type.name();
2305 if (!element.classList.contains(typeClassName)) {
2306 element.removeMatchingStyleClasses("network-type-\\w+");
2307 element.classList.add(typeClassName);
2311 _refreshResponseHeaderCell: function(headerName)
2313 var cell = this._responseHeaderCells[headerName];
2314 var value = this._request.responseHeaderValue(headerName);
2315 cell.setTextAndTitle(value ? value : "");
2318 _refreshNameCell: function()
2320 this._nameCell.removeChildren();
2322 if (this._request.type === WebInspector.resourceTypes.Image) {
2323 var previewImage = document.createElement("img");
2324 previewImage.className = "image-network-icon-preview";
2325 this._request.populateImageSource(previewImage);
2327 var iconElement = document.createElement("div");
2328 iconElement.className = "icon";
2329 iconElement.appendChild(previewImage);
2331 var iconElement = document.createElement("img");
2332 iconElement.className = "icon";
2334 this._nameCell.appendChild(iconElement);
2335 this._nameCell.appendChild(document.createTextNode(this._request.name()));
2336 this._appendSubtitle(this._nameCell, this._request.path());
2337 this._nameCell.title = this._request.url;
2340 _refreshMethodCell: function()
2342 this._methodCell.setTextAndTitle(this._request.requestMethod);
2345 _refreshStatusCell: function()
2347 this._statusCell.removeChildren();
2349 if (this._request.failed) {
2350 var failText = this._request.canceled ? WebInspector.UIString("(canceled)") : WebInspector.UIString("(failed)");
2351 if (this._request.localizedFailDescription) {
2352 this._statusCell.appendChild(document.createTextNode(failText));
2353 this._appendSubtitle(this._statusCell, this._request.localizedFailDescription);
2354 this._statusCell.title = failText + " " + this._request.localizedFailDescription;
2356 this._statusCell.setTextAndTitle(failText);
2357 this._statusCell.classList.add("network-dim-cell");
2361 this._statusCell.classList.remove("network-dim-cell");
2363 if (this._request.statusCode) {
2364 this._statusCell.appendChild(document.createTextNode("" + this._request.statusCode));
2365 this._appendSubtitle(this._statusCell, this._request.statusText);
2366 this._statusCell.title = this._request.statusCode + " " + this._request.statusText;
2367 if (this._request.cached)
2368 this._statusCell.classList.add("network-dim-cell");
2370 if (this._request.parsedURL.isDataURL())
2371 this._statusCell.setTextAndTitle(WebInspector.UIString("(data)"));
2372 else if (this._request.isPingRequest())
2373 this._statusCell.setTextAndTitle(WebInspector.UIString("(ping)"));
2374 else if (this._request.finished)
2375 this._statusCell.setTextAndTitle(WebInspector.UIString("Finished"));
2377 this._statusCell.setTextAndTitle(WebInspector.UIString("(pending)"));
2378 this._statusCell.classList.add("network-dim-cell");
2382 _refreshSchemeCell: function()
2384 this._schemeCell.setTextAndTitle(this._request.scheme);
2387 _refreshDomainCell: function()
2389 this._domainCell.setTextAndTitle(this._request.domain);
2392 _refreshTypeCell: function()
2394 if (this._request.mimeType) {
2395 this._typeCell.classList.remove("network-dim-cell");
2396 this._typeCell.setTextAndTitle(this._request.mimeType);
2398 this._typeCell.enableStyleClass("network-dim-cell", !this._request.isPingRequest());
2399 this._typeCell.setTextAndTitle(this._request.requestContentType() || "");
2403 _refreshInitiatorCell: function()
2405 this._initiatorCell.removeChildren();
2406 this._initiatorCell.classList.remove("network-dim-cell");
2407 this._initiatorCell.classList.remove("network-script-initiated");
2408 delete this._initiatorCell.request;
2410 var request = this._request;
2411 var initiator = request.initiatorInfo();
2413 switch (initiator.type) {
2414 case WebInspector.NetworkRequest.InitiatorType.Parser:
2415 this._initiatorCell.title = initiator.url + ":" + initiator.lineNumber;
2416 this._initiatorCell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2417 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Parser"));
2420 case WebInspector.NetworkRequest.InitiatorType.Redirect:
2421 this._initiatorCell.title = initiator.url;
2422 console.assert(request.redirectSource);
2423 var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2424 this._initiatorCell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2425 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Redirect"));
2428 case WebInspector.NetworkRequest.InitiatorType.Script:
2429 var urlElement = this._linkifier.linkifyLocation(initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2430 urlElement.title = "";
2431 this._initiatorCell.appendChild(urlElement);
2432 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
2433 this._initiatorCell.classList.add("network-script-initiated");
2434 this._initiatorCell.request = request;
2438 this._initiatorCell.title = "";
2439 this._initiatorCell.classList.add("network-dim-cell");
2440 this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
2444 _refreshCookiesCell: function()
2446 var requestCookies = this._request.requestCookies;
2447 this._cookiesCell.setTextAndTitle(requestCookies ? "" + requestCookies.length : "");
2450 _refreshSetCookiesCell: function()
2452 var responseCookies = this._request.responseCookies;
2453 this._setCookiesCell.setTextAndTitle(responseCookies ? "" + responseCookies.length : "");
2456 _refreshSizeCell: function()
2458 if (this._request.cached) {
2459 this._sizeCell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2460 this._sizeCell.classList.add("network-dim-cell");
2462 var resourceSize = Number.bytesToString(this._request.resourceSize);
2463 var transferSize = Number.bytesToString(this._request.transferSize);
2464 this._sizeCell.setTextAndTitle(transferSize);
2465 this._sizeCell.classList.remove("network-dim-cell");
2466 this._appendSubtitle(this._sizeCell, resourceSize);
2470 _refreshTimeCell: function()
2472 if (this._request.duration > 0) {
2473 this._timeCell.classList.remove("network-dim-cell");
2474 this._timeCell.setTextAndTitle(Number.secondsToString(this._request.duration));
2475 this._appendSubtitle(this._timeCell, Number.secondsToString(this._request.latency));
2477 this._timeCell.classList.add("network-dim-cell");
2478 this._timeCell.setTextAndTitle(WebInspector.UIString("Pending"));
2482 _appendSubtitle: function(cellElement, subtitleText)
2484 var subtitleElement = document.createElement("div");
2485 subtitleElement.className = "network-cell-subtitle";
2486 subtitleElement.textContent = subtitleText;
2487 cellElement.appendChild(subtitleElement);
2490 refreshGraph: function(calculator)
2492 var percentages = calculator.computeBarGraphPercentages(this._request);
2493 this._percentages = percentages;
2495 this._barAreaElement.classList.remove("hidden");
2496 this._updateElementStyleClasses(this._timelineCell);
2498 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2499 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2501 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
2502 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2504 var labels = calculator.computeBarGraphLabels(this._request);
2505 this._labelLeftElement.textContent = labels.left;
2506 this._labelRightElement.textContent = labels.right;
2508 var tooltip = (labels.tooltip || "");
2509 this._barLeftElement.title = tooltip;
2510 this._labelLeftElement.title = tooltip;
2511 this._labelRightElement.title = tooltip;
2512 this._barRightElement.title = tooltip;
2515 _refreshLabelPositions: function()
2517 if (!this._percentages)
2519 this._labelLeftElement.style.removeProperty("left");
2520 this._labelLeftElement.style.removeProperty("right");
2521 this._labelLeftElement.classList.remove("before");
2522 this._labelLeftElement.classList.remove("hidden");
2524 this._labelRightElement.style.removeProperty("left");
2525 this._labelRightElement.style.removeProperty("right");
2526 this._labelRightElement.classList.remove("after");
2527 this._labelRightElement.classList.remove("hidden");
2529 const labelPadding = 10;
2530 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2531 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2533 if (this._barLeftElement) {
2534 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2535 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2537 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2538 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2541 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2542 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2544 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2545 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2546 const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2548 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2549 var leftHidden = true;
2551 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2552 var rightHidden = true;
2554 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2555 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2556 if (labelBefore && !labelAfter)
2558 else if (labelAfter && !labelBefore)
2564 this._labelLeftElement.classList.add("hidden");
2565 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2566 this._labelLeftElement.classList.add("before");
2568 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2569 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2574 this._labelRightElement.classList.add("hidden");
2575 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2576 this._labelRightElement.classList.add("after");
2578 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2579 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2583 __proto__: WebInspector.DataGridNode.prototype
2586 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2588 var aFileName = a._request.name();
2589 var bFileName = b._request.name();
2590 if (aFileName > bFileName)
2592 if (bFileName > aFileName)
2597 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2599 if (b._request.cached && !a._request.cached)
2601 if (a._request.cached && !b._request.cached)
2604 return a._request.transferSize - b._request.transferSize;
2607 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2609 var aInitiator = a._request.initiatorInfo();
2610 var bInitiator = b._request.initiatorInfo();
2612 if (aInitiator.type < bInitiator.type)
2614 if (aInitiator.type > bInitiator.type)
2617 if (aInitiator.source < bInitiator.source)
2619 if (aInitiator.source > bInitiator.source)
2622 if (aInitiator.lineNumber < bInitiator.lineNumber)
2624 if (aInitiator.lineNumber > bInitiator.lineNumber)
2627 if (aInitiator.columnNumber < bInitiator.columnNumber)
2629 if (aInitiator.columnNumber > bInitiator.columnNumber)
2635 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
2637 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
2638 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
2639 return aScore - bScore;
2642 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
2644 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
2645 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
2646 return aScore - bScore;
2649 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
2651 var aValue = a._request[propertyName];
2652 var bValue = b._request[propertyName];
2653 if (aValue > bValue)
2654 return revert ? -1 : 1;
2655 if (bValue > aValue)
2656 return revert ? 1 : -1;