blob: 60163bb68638b6b3dec7c62ec38876ce50fa45e1 [file] [log] [blame]
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @implements {WebInspector.Searchable}
* @implements {WebInspector.TargetManager.Observer}
* @extends {WebInspector.VBox}
* @param {!WebInspector.FilterBar} filterBar
* @param {!Element} progressBarContainer
* @param {!WebInspector.Setting} networkLogLargeRowsSetting
*/
WebInspector.NetworkLogView = function(filterBar, progressBarContainer, networkLogLargeRowsSetting)
{
WebInspector.VBox.call(this);
this.setMinimumSize(50, 64);
this.registerRequiredCSS("network/networkLogView.css");
this.registerRequiredCSS("ui/filter.css");
this._networkHideDataURLSetting = WebInspector.settings.createSetting("networkHideDataURL", false);
this._networkResourceTypeFiltersSetting = WebInspector.settings.createSetting("networkResourceTypeFilters", {});
this._networkShowPrimaryLoadWaterfallSetting = WebInspector.settings.createSetting("networkShowPrimaryLoadWaterfall", false);
this._filterBar = filterBar;
this._progressBarContainer = progressBarContainer;
this._networkLogLargeRowsSetting = networkLogLargeRowsSetting;
var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisibility;
this._columnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
var savedColumnsVisibility = this._columnsVisibilitySetting.get();
var columnsVisibility = {};
for (var columnId in defaultColumnsVisibility)
columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
this._columnsVisibilitySetting.set(columnsVisibility);
/** @type {!Map.<string, !WebInspector.NetworkDataGridNode>} */
this._nodesByRequestId = new Map();
/** @type {!Object.<string, boolean>} */
this._staleRequestIds = {};
/** @type {number} */
this._mainRequestLoadTime = -1;
/** @type {number} */
this._mainRequestDOMContentLoadedTime = -1;
this._matchedRequestCount = 0;
/** @type {!Array<{time: number, element: !Element}>} */
this._eventDividers = [];
this._highlightedSubstringChanges = [];
/** @type {!Array.<!WebInspector.NetworkLogView.Filter>} */
this._filters = [];
/** @type {?WebInspector.NetworkLogView.Filter} */
this._timeFilter = null;
this._currentMatchedRequestNode = null;
this._currentMatchedRequestIndex = -1;
/** @type {!WebInspector.Linkifier} */
this._popupLinkifier = new WebInspector.Linkifier();
/** @type {!WebInspector.Linkifier} */
this.linkifier = new WebInspector.Linkifier();
this._gridMode = true;
this._recording = false;
this._preserveLog = false;
/** @type {number} */
this._rowHeight = 0;
this._addFilters();
this._resetSuggestionBuilder();
this._initializeView();
WebInspector.moduleSetting("networkColorCodeResourceTypes").addChangeListener(this._invalidateAllItems, this);
this._networkLogLargeRowsSetting.addChangeListener(this._updateRowsSize, this);
WebInspector.targetManager.observeTargets(this);
WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
}
WebInspector.NetworkLogView._isFilteredOutSymbol = Symbol("isFilteredOut");
WebInspector.NetworkLogView._isMatchingSearchQuerySymbol = Symbol("isMatchingSearchQuery");
WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
WebInspector.NetworkLogView._responseHeaderColumns = ["Cache-Control", "Connection", "Content-Encoding", "Content-Length", "ETag", "Keep-Alive", "Last-Modified", "Server", "Vary"];
WebInspector.NetworkLogView._defaultColumnsVisibility = {
method: false, status: true, protocol: false, scheme: false, domain: false, remoteAddress: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true, priority: false, connectionId: false,
"Cache-Control": false, "Connection": false, "Content-Encoding": false, "Content-Length": false, "ETag": false, "Keep-Alive": false, "Last-Modified": false, "Server": false, "Vary": false
};
WebInspector.NetworkLogView._defaultRefreshDelay = 200;
WebInspector.NetworkLogView._waterfallMinOvertime = 1;
WebInspector.NetworkLogView._waterfallMaxOvertime = 3;
/** @enum {string} */
WebInspector.NetworkLogView.FilterType = {
Domain: "domain",
HasResponseHeader: "has-response-header",
Is: "is",
LargerThan: "larger-than",
Method: "method",
MimeType: "mime-type",
MixedContent: "mixed-content",
Scheme: "scheme",
SetCookieDomain: "set-cookie-domain",
SetCookieName: "set-cookie-name",
SetCookieValue: "set-cookie-value",
StatusCode: "status-code"
};
/** @enum {string} */
WebInspector.NetworkLogView.MixedContentFilterValues = {
All: "all",
Displayed: "displayed",
Blocked: "blocked",
BlockOverridden: "block-overridden"
}
/** @enum {string} */
WebInspector.NetworkLogView.IsFilterType = {
Running: "running"
};
/** @type {!Array.<string>} */
WebInspector.NetworkLogView._searchKeys = Object.values(WebInspector.NetworkLogView.FilterType);
/** @type {!Object.<string, string>} */
WebInspector.NetworkLogView._columnTitles = {
"name": WebInspector.UIString("Name"),
"method": WebInspector.UIString("Method"),
"status": WebInspector.UIString("Status"),
"protocol": WebInspector.UIString("Protocol"),
"scheme": WebInspector.UIString("Scheme"),
"domain": WebInspector.UIString("Domain"),
"remoteAddress": WebInspector.UIString("Remote Address"),
"type": WebInspector.UIString("Type"),
"initiator": WebInspector.UIString("Initiator"),
"cookies": WebInspector.UIString("Cookies"),
"setCookies": WebInspector.UIString("Set-Cookies"),
"size": WebInspector.UIString("Size"),
"time": WebInspector.UIString("Time"),
"connectionId": WebInspector.UIString("Connection Id"),
"priority": WebInspector.UIString("Priority"),
"timeline": WebInspector.UIString("Timeline"),
// Response header columns
"Cache-Control": WebInspector.UIString("Cache-Control"),
"Connection": WebInspector.UIString("Connection"),
"Content-Encoding": WebInspector.UIString("Content-Encoding"),
"Content-Length": WebInspector.UIString("Content-Length"),
"ETag": WebInspector.UIString("ETag"),
"Keep-Alive": WebInspector.UIString("Keep-Alive"),
"Last-Modified": WebInspector.UIString("Last-Modified"),
"Server": WebInspector.UIString("Server"),
"Vary": WebInspector.UIString("Vary")
};
WebInspector.NetworkLogView.prototype = {
/**
* @param {boolean} recording
*/
setRecording: function(recording)
{
this._recording = recording;
this._updateSummaryBar();
},
/**
* @param {boolean} preserveLog
*/
setPreserveLog: function(preserveLog)
{
this._preserveLog = preserveLog;
},
/**
* @override
* @param {!WebInspector.Target} target
*/
targetAdded: function(target)
{
target.networkLog.requests().forEach(this._appendRequest.bind(this));
},
/**
* @override
* @param {!WebInspector.Target} target
*/
targetRemoved: function(target)
{
},
/**
* @param {number} start
* @param {number} end
*/
setWindow: function(start, end)
{
if (!start && !end) {
this._timeFilter = null;
this._timeCalculator.setWindow(null);
} else {
this._timeFilter = WebInspector.NetworkLogView._requestTimeFilter.bind(null, start, end);
this._timeCalculator.setWindow(new WebInspector.NetworkTimeBoundary(start, end));
}
this._updateDividersIfNeeded();
this._filterRequests();
},
clearSelection: function()
{
if (this._dataGrid.selectedNode)
this._dataGrid.selectedNode.deselect();
},
_addFilters: function()
{
this._textFilterUI = new WebInspector.TextFilterUI();
this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
this._filterBar.addFilter(this._textFilterUI);
var dataURLSetting = this._networkHideDataURLSetting;
this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
this._filterBar.addFilter(this._dataURLFilterUI);
var filterItems = [];
for (var categoryId in WebInspector.resourceCategories) {
var category = WebInspector.resourceCategories[categoryId];
filterItems.push({name: category.title, label: category.shortTitle, title: category.title});
}
this._resourceCategoryFilterUI = new WebInspector.NamedBitSetFilterUI(filterItems, this._networkResourceTypeFiltersSetting);
this._resourceCategoryFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
this._filterBar.addFilter(this._resourceCategoryFilterUI);
},
_resetSuggestionBuilder: function()
{
this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkLogView._searchKeys);
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Is, WebInspector.NetworkLogView.IsFilterType.Running);
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.LargerThan, "100");
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.LargerThan, "10k");
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.LargerThan, "1M");
this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
},
/**
* @param {!WebInspector.Event} event
*/
_filterChanged: function(event)
{
this._removeAllNodeHighlights();
this._parseFilterQuery(this._textFilterUI.value());
this._filterRequests();
},
_initializeView: function()
{
this.element.id = "network-container";
this._createSortingFunctions();
this._createCalculators();
this._createTable();
this._createTimelineGrid();
this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
this._updateRowsSize();
this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
this.switchViewMode(true);
},
_showRecordingHint: function()
{
this._hideRecordingHint();
this._recordingHint = this.element.createChild("div", "network-status-pane fill");
var hintText = this._recordingHint.createChild("div", "recording-hint");
var reloadShortcutNode = this._recordingHint.createChild("b");
reloadShortcutNode.textContent = WebInspector.ShortcutsScreen.TimelinePanelShortcuts.RecordPageReload[0].name;
if (this._recording) {
var recordingText = hintText.createChild("span");
recordingText.textContent = WebInspector.UIString("Recording network activity\u2026");
hintText.createChild("br");
hintText.appendChild(WebInspector.formatLocalized(WebInspector.UIString("Perform a request or hit %s to record the reload."), [reloadShortcutNode], null));
} else {
var recordNode = hintText.createChild("b");
recordNode.textContent = WebInspector.shortcutRegistry.shortcutTitleForAction("network.toggle-recording");
hintText.appendChild(WebInspector.formatLocalized(WebInspector.UIString("Record (%s) or reload (%s) to display network activity."), [recordNode, reloadShortcutNode], null));
}
},
_hideRecordingHint: function()
{
if (this._recordingHint)
this._recordingHint.remove();
delete this._recordingHint;
},
/**
* @override
* @return {!Array.<!Element>}
*/
elementsToRestoreScrollPositionsFor: function()
{
if (!this._dataGrid) // Not initialized yet.
return [];
return [this._dataGrid.scrollContainer];
},
_createTimelineGrid: function()
{
this._timelineGrid = new WebInspector.TimelineGrid();
this._timelineGrid.element.classList.add("network-timeline-grid");
this._dataGrid.element.appendChild(this._timelineGrid.element);
},
_createTable: function()
{
var columns = [];
columns.push({
id: "name",
titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
title: WebInspector.NetworkLogView._columnTitles["name"],
weight: 20
});
columns.push({
id: "method",
title: WebInspector.NetworkLogView._columnTitles["method"],
weight: 6
});
columns.push({
id: "status",
titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
title: WebInspector.NetworkLogView._columnTitles["status"],
weight: 6
});
columns.push({
id: "protocol",
title: WebInspector.NetworkLogView._columnTitles["protocol"],
weight: 6
});
columns.push({
id: "scheme",
title: WebInspector.NetworkLogView._columnTitles["scheme"],
weight: 6
});
columns.push({
id: "domain",
title: WebInspector.NetworkLogView._columnTitles["domain"],
weight: 6
});
columns.push({
id: "remoteAddress",
title: WebInspector.NetworkLogView._columnTitles["remoteAddress"],
weight: 10,
align: WebInspector.DataGrid.Align.Right
});
columns.push({
id: "type",
title: WebInspector.NetworkLogView._columnTitles["type"],
weight: 6
});
columns.push({
id: "initiator",
title: WebInspector.NetworkLogView._columnTitles["initiator"],
weight: 10
});
columns.push({
id: "cookies",
title: WebInspector.NetworkLogView._columnTitles["cookies"],
weight: 6,
align: WebInspector.DataGrid.Align.Right
});
columns.push({
id: "setCookies",
title: WebInspector.NetworkLogView._columnTitles["setCookies"],
weight: 6,
align: WebInspector.DataGrid.Align.Right
});
columns.push({
id: "size",
titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
title: WebInspector.NetworkLogView._columnTitles["size"],
weight: 6,
align: WebInspector.DataGrid.Align.Right
});
columns.push({
id: "time",
titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
title: WebInspector.NetworkLogView._columnTitles["time"],
weight: 6,
align: WebInspector.DataGrid.Align.Right
});
columns.push({
id: "priority",
title: WebInspector.NetworkLogView._columnTitles["priority"],
weight: 6
});
columns.push({
id: "connectionId",
title: WebInspector.NetworkLogView._columnTitles["connectionId"],
weight: 6
});
var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
for (var i = 0; i < responseHeaderColumns.length; ++i) {
var headerName = responseHeaderColumns[i];
var descriptor = {
id: headerName,
title: WebInspector.NetworkLogView._columnTitles[headerName],
weight: 6
};
if (headerName === "Content-Length")
descriptor.align = WebInspector.DataGrid.Align.Right;
columns.push(descriptor);
}
columns.push({
id: "timeline",
title: WebInspector.NetworkLogView._columnTitles["timeline"],
sortable: false,
weight: 40,
sort: WebInspector.DataGrid.Order.Ascending
});
for (var column of columns) {
column.sortable = column.id !== "timeline";
column.nonSelectable = column.id !== "name";
}
this._dataGrid = new WebInspector.SortableDataGrid(columns);
this._dataGrid.setStickToBottom(true);
this._updateColumns();
this._dataGrid.setName("networkLog");
this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
this._dataGrid.element.classList.add("network-log-grid");
this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
this._dataGrid.element.addEventListener("mousedown", this._dataGridMouseDown.bind(this), true);
this._dataGrid.element.addEventListener("mousemove", this._dataGridMouseMove.bind(this), true);
this._dataGrid.element.addEventListener("mouseleave", this._highlightInitiatorChain.bind(this, null), true);
this._dataGrid.show(this.element);
// Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
this._patchTimelineHeader();
this._dataGrid.sortNodes(this._sortingFunctions.startTime, false);
},
/**
* @param {!Event} event
*/
_dataGridMouseDown: function(event)
{
if ((!this._dataGrid.selectedNode && event.button) || event.target.enclosingNodeOrSelfWithNodeName("a"))
event.consume();
},
/**
* @param {!Event} event
*/
_dataGridMouseMove: function(event)
{
var node = event.shiftKey ? this._dataGrid.dataGridNodeFromNode(event.target) : null;
this._highlightInitiatorChain(node ? node.request() : null);
},
/**
* @param {?WebInspector.NetworkRequest} request
*/
_highlightInitiatorChain: function(request)
{
if (this._requestWithHighlightedInitiators === request)
return;
this._requestWithHighlightedInitiators = request;
if (!request) {
for (var node of this._nodesByRequestId.values()) {
if (!node.dataGrid)
continue;
node.element().classList.remove("network-node-on-initiator-path", "network-node-on-initiated-path");
}
return;
}
var initiators = request.initiatorChain();
var initiated = new Set();
for (var node of this._nodesByRequestId.values()) {
if (!node.dataGrid)
continue;
var localInitiators = node.request().initiatorChain();
if (localInitiators.has(request))
initiated.add(node.request());
}
for (var node of this._nodesByRequestId.values()) {
if (!node.dataGrid)
continue;
node.element().classList.toggle("network-node-on-initiator-path", node.request() !== request && initiators.has(node.request()));
node.element().classList.toggle("network-node-on-initiated-path", node.request() !== request && initiated.has(node.request()));
}
},
/**
* @param {string} title
* @param {string} subtitle
* @return {!DocumentFragment}
*/
_makeHeaderFragment: function(title, subtitle)
{
var fragment = createDocumentFragment();
fragment.createTextChild(title);
var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
subtitleDiv.createTextChild(subtitle);
return fragment;
},
_patchTimelineHeader: function()
{
var timelineSorting = createElement("select");
var option = createElement("option");
option.value = "startTime";
option.label = WebInspector.UIString("Timeline");
option.disabled = true;
timelineSorting.appendChild(option);
option = createElement("option");
option.value = "startTime";
option.label = WebInspector.UIString("Timeline \u2013 Start Time");
option.sortOrder = WebInspector.DataGrid.Order.Ascending;
timelineSorting.appendChild(option);
option = createElement("option");
option.value = "responseTime";
option.label = WebInspector.UIString("Timeline \u2013 Response Time");
option.sortOrder = WebInspector.DataGrid.Order.Ascending;
timelineSorting.appendChild(option);
option = createElement("option");
option.value = "endTime";
option.label = WebInspector.UIString("Timeline \u2013 End Time");
option.sortOrder = WebInspector.DataGrid.Order.Ascending;
timelineSorting.appendChild(option);
option = createElement("option");
option.value = "duration";
option.label = WebInspector.UIString("Timeline \u2013 Total Duration");
option.sortOrder = WebInspector.DataGrid.Order.Descending;
timelineSorting.appendChild(option);
option = createElement("option");
option.value = "latency";
option.label = WebInspector.UIString("Timeline \u2013 Latency");
option.sortOrder = WebInspector.DataGrid.Order.Descending;
timelineSorting.appendChild(option);
var header = this._dataGrid.headerTableHeader("timeline");
header.replaceChild(timelineSorting, header.firstChild);
header.createChild("div", "sort-order-icon-container").createChild("div", "sort-order-icon");
timelineSorting.selectedIndex = 1;
timelineSorting.addEventListener("click", function(event) { event.consume(); }, false);
timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
this._timelineSortSelector = timelineSorting;
},
_createSortingFunctions: function()
{
this._sortingFunctions = {};
this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
this._sortingFunctions.protocol = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "protocol", false);
this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
this._sortingFunctions.connectionId = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "connectionId", false);
this._sortingFunctions.priority = WebInspector.NetworkDataGridNode.InitialPriorityComparator;
this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
},
_createCalculators: function()
{
/** @type {!WebInspector.NetworkTransferTimeCalculator} */
this._timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
/** @type {!WebInspector.NetworkTransferDurationCalculator} */
this._durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
/** @type {!Object.<string, !WebInspector.NetworkTimeCalculator>} */
this._calculators = {};
this._calculators.timeline = this._timeCalculator;
this._calculators.startTime = this._timeCalculator;
this._calculators.endTime = this._timeCalculator;
this._calculators.responseTime = this._timeCalculator;
this._calculators.duration = this._durationCalculator;
this._calculators.latency = this._durationCalculator;
this._calculator = this._timeCalculator;
},
_sortItems: function()
{
this._removeAllNodeHighlights();
var columnIdentifier = this._dataGrid.sortColumnIdentifier();
if (columnIdentifier === "timeline") {
this._sortByTimeline();
return;
}
var sortingFunction = this._sortingFunctions[columnIdentifier];
if (!sortingFunction)
return;
this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
this._timelineSortSelector.selectedIndex = 0;
},
_sortByTimeline: function()
{
this._removeAllNodeHighlights();
var selectedIndex = this._timelineSortSelector.selectedIndex;
if (!selectedIndex)
selectedIndex = 1; // Sort by start time by default.
var selectedOption = this._timelineSortSelector[selectedIndex];
var value = selectedOption.value;
this._setCalculator(this._calculators[value]);
var sortingFunction = this._sortingFunctions[value];
this._dataGrid.sortNodes(sortingFunction);
this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
this._dataGrid.markColumnAsSortedBy("timeline", selectedOption.sortOrder);
},
_updateSummaryBar: function()
{
var requestsNumber = this._nodesByRequestId.size;
if (!requestsNumber) {
this._showRecordingHint();
return;
}
this._hideRecordingHint();
var transferSize = 0;
var selectedRequestsNumber = 0;
var selectedTransferSize = 0;
var baseTime = -1;
var maxTime = -1;
var nodes = this._nodesByRequestId.valuesArray();
for (var i = 0; i < nodes.length; ++i) {
var request = nodes[i].request();
var requestTransferSize = request.transferSize;
transferSize += requestTransferSize;
if (!nodes[i][WebInspector.NetworkLogView._isFilteredOutSymbol]) {
selectedRequestsNumber++;
selectedTransferSize += requestTransferSize;
}
if (request.url === request.target().resourceTreeModel.inspectedPageURL() && request.resourceType() === WebInspector.resourceTypes.Document)
baseTime = request.startTime;
if (request.endTime > maxTime)
maxTime = request.endTime;
}
var summaryBar = this._summaryBarElement;
summaryBar.removeChildren();
var separator = "\u2002\u2758\u2002";
var text = "";
/**
* @param {string} chunk
* @return {!Element}
*/
function appendChunk(chunk)
{
var span = summaryBar.createChild("span");
span.textContent = chunk;
text += chunk;
return span;
}
if (selectedRequestsNumber !== requestsNumber) {
appendChunk(WebInspector.UIString("%d / %d requests", selectedRequestsNumber, requestsNumber));
appendChunk(separator);
appendChunk(WebInspector.UIString("%s / %s transferred", Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize)));
} else {
appendChunk(WebInspector.UIString("%d requests", requestsNumber));
appendChunk(separator);
appendChunk(WebInspector.UIString("%s transferred", Number.bytesToString(transferSize)));
}
if (baseTime !== -1) {
appendChunk(separator);
appendChunk(WebInspector.UIString("Finish: %s", Number.secondsToString(maxTime - baseTime)));
if (this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
appendChunk(separator);
var domContentLoadedText = WebInspector.UIString("DOMContentLoaded: %s", Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
appendChunk(domContentLoadedText).classList.add("summary-blue");
}
if (this._mainRequestLoadTime !== -1) {
appendChunk(separator);
var loadText = WebInspector.UIString("Load: %s", Number.secondsToString(this._mainRequestLoadTime - baseTime));
appendChunk(loadText).classList.add("summary-red");
}
}
summaryBar.title = text;
},
_scheduleRefresh: function()
{
if (this._needsRefresh)
return;
this._needsRefresh = true;
if (this.isShowing() && !this._refreshTimeout)
this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
},
_updateDividersIfNeeded: function()
{
if (!this.isShowing()) {
this._scheduleRefresh();
return;
}
var timelineOffset = this._dataGrid.columnOffset("timeline");
// Position timline grid location.
if (timelineOffset)
this._timelineGrid.element.style.left = timelineOffset + "px";
var calculator = this.calculator();
calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
this._timelineGrid.updateDividers(calculator, 75);
if (calculator.startAtZero) {
// If our current sorting method starts at zero, that means it shows all
// requests starting at the same point, and so onLoad event and DOMContent
// event lines really wouldn't make much sense here, so don't render them.
return;
}
this._updateEventDividers();
},
/**
* @param {!Array<number>} times
*/
addFilmStripFrames: function(times)
{
this._addEventDividers(times, "network-frame-divider");
},
/**
* @param {number} time
*/
selectFilmStripFrame: function(time)
{
for (var divider of this._eventDividers)
divider.element.classList.toggle("network-frame-divider-selected", divider.time === time);
},
clearFilmStripFrame: function()
{
for (var divider of this._eventDividers)
divider.element.classList.toggle("network-frame-divider-selected", false);
},
/**
* @param {!Array<number>} times
* @param {string} className
*/
_addEventDividers: function(times, className)
{
for (var i = 0; i < times.length; ++i) {
var element = createElementWithClass("div", "network-event-divider " + className);
this._timelineGrid.addEventDivider(element);
this._eventDividers.push({time: times[i], element: element});
}
// Update event dividers immediately
this._updateEventDividers();
// Schedule refresh in case dividers change the calculator span.
this._scheduleRefresh();
},
_updateEventDividers: function()
{
var calculator = this.calculator();
for (var divider of this._eventDividers) {
var timePercent = calculator.computePercentageFromEventTime(divider.time);
divider.element.classList.toggle("invisible", timePercent < 0);
divider.element.style.left = timePercent + "%";
}
},
_refreshIfNeeded: function()
{
if (this._needsRefresh)
this.refresh();
},
_invalidateAllItems: function()
{
var requestIds = this._nodesByRequestId.keysArray();
for (var i = 0; i < requestIds.length; ++i)
this._staleRequestIds[requestIds[i]] = true;
this.refresh();
},
/**
* @return {!WebInspector.NetworkTimeCalculator}
*/
timeCalculator: function()
{
return this._timeCalculator;
},
/**
* @return {!WebInspector.NetworkTimeCalculator}
*/
calculator: function()
{
return this._calculator;
},
/**
* @param {!WebInspector.NetworkTimeCalculator} x
*/
_setCalculator: function(x)
{
if (!x || this._calculator === x)
return;
this._calculator = x;
this._calculator.reset();
if (this._calculator.startAtZero)
this._timelineGrid.hideEventDividers();
else
this._timelineGrid.showEventDividers();
this._invalidateAllItems();
},
/**
* @param {!WebInspector.Event} event
*/
_loadEventFired: function(event)
{
if (!this._recording)
return;
var data = /** @type {number} */ (event.data);
if (data) {
this._mainRequestLoadTime = data;
this._addEventDividers([data], "network-red-divider");
}
},
/**
* @param {!WebInspector.Event} event
*/
_domContentLoadedEventFired: function(event)
{
if (!this._recording)
return;
var data = /** @type {number} */ (event.data);
if (data) {
this._mainRequestDOMContentLoadedTime = data;
this._addEventDividers([data], "network-blue-divider");
}
},
wasShown: function()
{
this._refreshIfNeeded();
},
willHide: function()
{
this._popoverHelper.hidePopover();
},
refresh: function()
{
this._needsRefresh = false;
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
delete this._refreshTimeout;
}
this._removeAllNodeHighlights();
var oldBoundary = this.calculator().boundary();
this._timeCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
this._durationCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
this._timeCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
this._durationCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
var dataGrid = this._dataGrid;
var rootNode = dataGrid.rootNode();
var nodesToInsert = [];
for (var requestId in this._staleRequestIds) {
var node = this._nodesByRequestId.get(requestId);
if (!node)
continue;
if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol])
rootNode.removeChild(node);
node[WebInspector.NetworkLogView._isFilteredOutSymbol] = !this._applyFilter(node);
if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol])
nodesToInsert.push(node);
var request = node.request();
this._timeCalculator.updateBoundaries(request);
this._durationCalculator.updateBoundaries(request);
}
for (var i = 0; i < nodesToInsert.length; ++i) {
var node = nodesToInsert[i];
var request = node.request();
node.refresh();
dataGrid.insertChild(node);
node[WebInspector.NetworkLogView._isMatchingSearchQuerySymbol] = this._matchRequest(request);
}
this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false);
if (this._shouldSetWaterfallWindow && this._mainRequestLoadTime !== -1) {
var waterfallWindow = this.calculator().boundary();
var overtime = this._mainRequestLoadTime - waterfallWindow.minimum;
overtime = Number.constrain(overtime, WebInspector.NetworkLogView._waterfallMinOvertime, WebInspector.NetworkLogView._waterfallMaxOvertime)
var waterfallEnd = this._mainRequestLoadTime + overtime;
if (waterfallEnd <= waterfallWindow.maximum) {
waterfallWindow.maximum = waterfallEnd;
this._shouldSetWaterfallWindow = false;
this._timeCalculator.setWindow(waterfallWindow);
}
}
if (!this.calculator().boundary().equals(oldBoundary)) {
// The boundaries changed, so all item graphs are stale.
this._updateDividersIfNeeded();
var nodes = this._nodesByRequestId.valuesArray();
for (var i = 0; i < nodes.length; ++i)
nodes[i].refreshGraph();
}
this._staleRequestIds = {};
this._updateSummaryBar();
},
reset: function()
{
this._requestWithHighlightedInitiators = null;
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, null);
/** @type {boolean} */
this._shouldSetWaterfallWindow = Runtime.experiments.isEnabled("showPrimaryLoadWaterfallInNetworkTimeline") && this._networkShowPrimaryLoadWaterfallSetting.get();
this._clearSearchMatchedList();
if (this._popoverHelper)
this._popoverHelper.hidePopover();
this._timeFilter = null;
this._calculator.reset();
this._timeCalculator.setWindow(null);
var nodes = this._nodesByRequestId.valuesArray();
for (var i = 0; i < nodes.length; ++i)
nodes[i].dispose();
this._nodesByRequestId.clear();
this._staleRequestIds = {};
this._resetSuggestionBuilder();
this._mainRequestLoadTime = -1;
this._mainRequestDOMContentLoadedTime = -1;
this._eventDividers = [];
this._timelineGrid.removeEventDividers();
if (this._dataGrid) {
this._dataGrid.rootNode().removeChildren();
this._updateDividersIfNeeded();
this._updateSummaryBar();
}
},
/**
* @param {!WebInspector.NetworkLogView.FilterType} filterType
* @param {string} filterValue
*/
setTextFilterValue: function(filterType, filterValue)
{
this._textFilterUI.setValue(filterType + ":" + filterValue);
},
/**
* @param {!WebInspector.Event} event
*/
_onRequestStarted: function(event)
{
if (!this._recording)
return;
var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
this._appendRequest(request);
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
_appendRequest: function(request)
{
var node = new WebInspector.NetworkDataGridNode(this, request);
node[WebInspector.NetworkLogView._isFilteredOutSymbol] = true;
node[WebInspector.NetworkLogView._isMatchingSearchQuerySymbol] = false;
// In case of redirect request id is reassigned to a redirected
// request and we need to update _nodesByRequestId and search results.
var originalRequestNode = this._nodesByRequestId.get(request.requestId);
if (originalRequestNode)
this._nodesByRequestId.set(originalRequestNode.request().requestId, originalRequestNode);
this._nodesByRequestId.set(request.requestId, node);
// Pull all the redirects of the main request upon commit load.
if (request.redirects) {
for (var i = 0; i < request.redirects.length; ++i)
this._refreshRequest(request.redirects[i]);
}
this._refreshRequest(request);
},
/**
* @param {!WebInspector.Event} event
*/
_onRequestUpdated: function(event)
{
var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
this._refreshRequest(request);
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
_refreshRequest: function(request)
{
if (!this._nodesByRequestId.get(request.requestId))
return;
WebInspector.NetworkLogView._subdomains(request.domain).forEach(this._suggestionBuilder.addItem.bind(this._suggestionBuilder, WebInspector.NetworkLogView.FilterType.Domain));
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Method, request.requestMethod);
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MimeType, request.mimeType);
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.Scheme, "" + request.scheme);
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.StatusCode, "" + request.statusCode);
if (request.mixedContentType !== "none") {
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MixedContent, WebInspector.NetworkLogView.MixedContentFilterValues.All);
}
if (request.mixedContentType === "optionally-blockable") {
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MixedContent, WebInspector.NetworkLogView.MixedContentFilterValues.Displayed);
}
if (request.mixedContentType === "blockable") {
var suggestion = request.wasBlocked() ? WebInspector.NetworkLogView.MixedContentFilterValues.Blocked : WebInspector.NetworkLogView.MixedContentFilterValues.BlockOverridden;
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.MixedContent, suggestion);
}
var responseHeaders = request.responseHeaders;
for (var i = 0, l = responseHeaders.length; i < l; ++i)
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.HasResponseHeader, responseHeaders[i].name);
var cookies = request.responseCookies;
for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
var cookie = cookies[i];
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieDomain, cookie.domain());
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieName, cookie.name());
this._suggestionBuilder.addItem(WebInspector.NetworkLogView.FilterType.SetCookieValue, cookie.value());
}
this._staleRequestIds[request.requestId] = true;
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.UpdateRequest, request);
this._scheduleRefresh();
},
/**
* @param {!WebInspector.Event} event
*/
_mainFrameNavigated: function(event)
{
if (!this._recording)
return;
var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
var loaderId = frame.loaderId;
// Pick provisional load requests.
var requestsToPick = [];
var requests = frame.target().networkLog.requests();
for (var i = 0; i < requests.length; ++i) {
var request = requests[i];
if (request.loaderId === loaderId)
requestsToPick.push(request);
}
if (!this._preserveLog) {
this.reset();
for (var i = 0; i < requestsToPick.length; ++i)
this._appendRequest(requestsToPick[i]);
}
for (var i = 0; i < requestsToPick.length; ++i) {
var request = requestsToPick[i];
var node = this._nodesByRequestId.get(request.requestId);
if (node) {
node.markAsNavigationRequest();
break;
}
}
},
/**
* @param {boolean} gridMode
*/
switchViewMode: function(gridMode)
{
if (this._gridMode === gridMode)
return;
this._gridMode = gridMode;
if (gridMode) {
if (this._dataGrid.selectedNode)
this._dataGrid.selectedNode.selected = false;
} else {
this._removeAllNodeHighlights();
this._popoverHelper.hidePopover();
}
this.element.classList.toggle("brief-mode", !gridMode);
this._updateColumns();
},
/**
* @return {number}
*/
rowHeight: function()
{
return this._rowHeight;
},
_updateRowsSize: function()
{
var largeRows = !!this._networkLogLargeRowsSetting.get();
this._rowHeight = largeRows ? 41 : 21;
this._dataGrid.element.classList.toggle("small", !largeRows);
this._timelineGrid.element.classList.toggle("small", !largeRows);
this._dataGrid.scheduleUpdate();
},
/**
* @param {!Element} element
* @param {!Event} event
* @return {!Element|!AnchorBox|undefined}
*/
_getPopoverAnchor: function(element, event)
{
if (!this._gridMode)
return;
var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
return anchor;
anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
if (anchor && anchor.request) {
var initiator = /** @type {!WebInspector.NetworkRequest} */ (anchor.request).initiator();
if (initiator && (initiator.stackTrace || initiator.asyncStackTrace))
return anchor;
}
},
/**
* @param {!Element} anchor
* @param {!WebInspector.Popover} popover
*/
_showPopover: function(anchor, popover)
{
var content;
if (anchor.classList.contains("network-script-initiated")) {
var request = /** @type {!WebInspector.NetworkRequest} */ (anchor.request);
var initiator = /** @type {!NetworkAgent.Initiator} */ (request.initiator());
content = WebInspector.DOMPresentationUtils.buildStackTracePreviewContents(request.target(), this._popupLinkifier, initiator.stackTrace, initiator.asyncStackTrace);
popover.setCanShrink(true);
} else {
content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request, this._timeCalculator.minimumBoundary());
popover.setCanShrink(false);
}
popover.showForAnchor(content, anchor);
},
_onHidePopover: function()
{
this._popupLinkifier.reset();
},
_updateColumns: function()
{
if (!this._dataGrid)
return;
var gridMode = this._gridMode;
var visibleColumns = {"name": true};
if (gridMode)
visibleColumns["timeline"] = true;
if (gridMode) {
var columnsVisibility = this._columnsVisibilitySetting.get();
for (var columnIdentifier in columnsVisibility)
visibleColumns[columnIdentifier] = columnsVisibility[columnIdentifier];
}
this._dataGrid.setColumnsVisiblity(visibleColumns);
},
/**
* @param {string} columnIdentifier
*/
_toggleColumnVisibility: function(columnIdentifier)
{
var columnsVisibility = this._columnsVisibilitySetting.get();
columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
this._columnsVisibilitySetting.set(columnsVisibility);
this._updateColumns();
},
/**
* @return {!Array.<string>}
*/
_getConfigurableColumnIDs: function()
{
if (this._configurableColumnIDs)
return this._configurableColumnIDs;
var columnTitles = WebInspector.NetworkLogView._columnTitles;
function compare(id1, id2)
{
return columnTitles[id1].compareTo(columnTitles[id2]);
}
var columnIDs = Object.keys(this._columnsVisibilitySetting.get());
this._configurableColumnIDs = columnIDs.sort(compare);
return this._configurableColumnIDs;
},
/**
* @param {!Event} event
*/
_contextMenu: function(event)
{
var contextMenu = new WebInspector.ContextMenu(event);
if (this._gridMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
var columnsVisibility = this._columnsVisibilitySetting.get();
var columnIDs = this._getConfigurableColumnIDs();
var columnTitles = WebInspector.NetworkLogView._columnTitles;
for (var i = 0; i < columnIDs.length; ++i) {
var columnIdentifier = columnIDs[i];
contextMenu.appendCheckboxItem(columnTitles[columnIdentifier], this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
}
contextMenu.show();
return;
}
var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
var request = gridNode && gridNode.request();
/**
* @param {string} url
*/
function openResourceInNewTab(url)
{
InspectorFrontendHost.openInNewTab(url);
}
if (request) {
contextMenu.appendApplicableItems(request);
if (request.requestHeadersText())
contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^request ^headers"), this._copyRequestHeaders.bind(this, request));
if (request.responseHeadersText)
contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^response ^headers"), this._copyResponseHeaders.bind(this, request));
if (request.finished)
contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^response"), this._copyResponse.bind(this, request));
if (WebInspector.isWin()) {
contextMenu.appendItem(WebInspector.UIString("Copy as cURL (cmd)"), this._copyCurlCommand.bind(this, request, "win"));
contextMenu.appendItem(WebInspector.UIString("Copy as cURL (bash)"), this._copyCurlCommand.bind(this, request, "unix"));
} else {
contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request, "unix"));
}
}
contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^all as HAR"), this._copyAll.bind(this));
contextMenu.appendSeparator();
contextMenu.appendItem(WebInspector.UIString.capitalize("Save as HAR with ^content"), this._exportAll.bind(this));
contextMenu.appendSeparator();
contextMenu.appendItem(WebInspector.UIString.capitalize("Clear ^browser ^cache"), this._clearBrowserCache.bind(this));
contextMenu.appendItem(WebInspector.UIString.capitalize("Clear ^browser ^cookies"), this._clearBrowserCookies.bind(this));
var blockedSetting = WebInspector.moduleSetting("blockedURLs");
if (request) {
contextMenu.appendSeparator();
var urlWithoutScheme = request.parsedURL.urlWithoutScheme();
if (urlWithoutScheme && blockedSetting.get().indexOf(urlWithoutScheme) === -1)
contextMenu.appendItem(WebInspector.UIString.capitalize("Block ^request URL"), addBlockedURL.bind(null, urlWithoutScheme));
var domain = request.parsedURL.domain();
if (domain && blockedSetting.get().indexOf(domain) === -1)
contextMenu.appendItem(WebInspector.UIString.capitalize("Block ^request ^domain"), addBlockedURL.bind(null, domain));
function addBlockedURL(url)
{
var list = blockedSetting.get();
list.push(url);
blockedSetting.set(list);
WebInspector.BlockedURLsPane.reveal();
}
}
if (request && request.resourceType() === WebInspector.resourceTypes.XHR) {
contextMenu.appendSeparator();
contextMenu.appendItem(WebInspector.UIString("Replay XHR"), request.replayXHR.bind(request));
contextMenu.appendSeparator();
}
contextMenu.show();
},
_harRequests: function()
{
var requests = this._nodesByRequestId.valuesArray().map(function(node) { return node.request(); });
var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
return httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
},
_copyAll: function()
{
var harArchive = {
log: (new WebInspector.HARLog(this._harRequests())).build()
};
InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
_copyRequestHeaders: function(request)
{
InspectorFrontendHost.copyText(request.requestHeadersText());
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
_copyResponse: function(request)
{
/**
* @param {?string} content
*/
function callback(content)
{
if (request.contentEncoded)
content = request.asDataURL();
InspectorFrontendHost.copyText(content || "");
}
request.requestContent(callback);
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
_copyResponseHeaders: function(request)
{
InspectorFrontendHost.copyText(request.responseHeadersText);
},
/**
* @param {!WebInspector.NetworkRequest} request
* @param {string} platform
*/
_copyCurlCommand: function(request, platform)
{
InspectorFrontendHost.copyText(this._generateCurlCommand(request, platform));
},
_exportAll: function()
{
var filename = WebInspector.targetManager.inspectedPageDomain() + ".har";
var stream = new WebInspector.FileOutputStream();
stream.open(filename, openCallback.bind(this));
/**
* @param {boolean} accepted
* @this {WebInspector.NetworkLogView}
*/
function openCallback(accepted)
{
if (!accepted)
return;
var progressIndicator = new WebInspector.ProgressIndicator();
this._progressBarContainer.appendChild(progressIndicator.element);
var harWriter = new WebInspector.HARWriter();
harWriter.write(stream, this._harRequests(), progressIndicator);
}
},
_clearBrowserCache: function()
{
if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?"))) {
var target = WebInspector.targetManager.mainTarget()
if (target)
target.networkManager.clearBrowserCache();
}
},
_clearBrowserCookies: function()
{
if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?"))) {
var target = WebInspector.targetManager.mainTarget()
if (target)
target.networkManager.clearBrowserCookies();
}
},
/**
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
_matchRequest: function(request)
{
var re = this._searchRegExp;
if (!re)
return false;
return re.test(request.name()) || (this._networkLogLargeRowsSetting.get() && re.test(request.path()));
},
_clearSearchMatchedList: function()
{
this._matchedRequestCount = -1;
this._currentMatchedRequestNode = null;
this._removeAllHighlights();
},
_removeAllHighlights: function()
{
this._removeAllNodeHighlights();
for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
this._highlightedSubstringChanges = [];
},
/**
* @param {number} n
* @param {boolean} reveal
*/
_highlightNthMatchedRequestForSearch: function(n, reveal)
{
this._removeAllHighlights();
/** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
var nodes = this._dataGrid.rootNode().children;
var matchCount = 0;
var node = null;
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i][WebInspector.NetworkLogView._isMatchingSearchQuerySymbol]) {
if (matchCount === n) {
node = nodes[i];
break;
}
matchCount++;
}
}
if (!node) {
this._currentMatchedRequestNode = null;
return;
}
var request = node.request();
if (reveal)
WebInspector.Revealer.reveal(request);
var highlightedSubstringChanges = node.highlightMatchedSubstring(this._searchRegExp);
this._highlightedSubstringChanges.push(highlightedSubstringChanges);
this._currentMatchedRequestNode = node;
this._currentMatchedRequestIndex = n;
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
},
/**
* @override
* @param {!WebInspector.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch: function(searchConfig, shouldJump, jumpBackwards)
{
var query = searchConfig.query;
var currentMatchedRequestNode = this._currentMatchedRequestNode;
this._clearSearchMatchedList();
this._searchRegExp = createPlainTextSearchRegex(query, "i");
/** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
var nodes = this._dataGrid.rootNode().children;
for (var i = 0; i < nodes.length; ++i)
nodes[i][WebInspector.NetworkLogView._isMatchingSearchQuerySymbol] = this._matchRequest(nodes[i].request());
var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
if (!newMatchedRequestIndex && jumpBackwards)
newMatchedRequestIndex = this._matchedRequestCount - 1;
this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
},
/**
* @override
* @return {boolean}
*/
supportsCaseSensitiveSearch: function()
{
return false;
},
/**
* @override
* @return {boolean}
*/
supportsRegexSearch: function()
{
return false;
},
/**
* @param {?WebInspector.NetworkDataGridNode} node
* @return {number}
*/
_updateMatchCountAndFindMatchIndex: function(node)
{
/** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
var nodes = this._dataGrid.rootNode().children;
var matchCount = 0;
var matchIndex = 0;
for (var i = 0; i < nodes.length; ++i) {
if (!nodes[i][WebInspector.NetworkLogView._isMatchingSearchQuerySymbol])
continue;
if (node === nodes[i])
matchIndex = matchCount;
matchCount++;
}
if (this._matchedRequestCount !== matchCount) {
this._matchedRequestCount = matchCount;
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
}
return matchIndex;
},
/**
* @param {number} index
* @return {number}
*/
_normalizeSearchResultIndex: function(index)
{
return (index + this._matchedRequestCount) % this._matchedRequestCount;
},
/**
* @param {!WebInspector.NetworkDataGridNode} node
* @return {boolean}
*/
_applyFilter: function(node)
{
var request = node.request();
if (this._timeFilter && !this._timeFilter(request))
return false;
var categoryName = request.resourceType().category().title;
if (!this._resourceCategoryFilterUI.accept(categoryName))
return false;
if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
return false;
for (var i = 0; i < this._filters.length; ++i) {
if (!this._filters[i](request))
return false;
}
return true;
},
/**
* @param {string} query
*/
_parseFilterQuery: function(query)
{
var parsedQuery = this._suggestionBuilder.parseQuery(query);
this._filters = parsedQuery.text.map(this._createTextFilter);
var n = parsedQuery.filters.length;
for (var i = 0; i < n; ++i) {
var filter = parsedQuery.filters[i];
var filterType = /** @type {!WebInspector.NetworkLogView.FilterType} */ (filter.type.toLowerCase());
this._filters.push(this._createFilter(filterType, filter.data, filter.negative));
}
},
/**
* @param {string} text
* @return {!WebInspector.NetworkLogView.Filter}
*/
_createTextFilter: function(text)
{
var negative = false;
if (text[0] === "-" && text.length > 1) {
negative = true;
text = text.substring(1);
}
var regexp = new RegExp(text.escapeForRegExp(), "i");
var filter = WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
if (negative)
filter = WebInspector.NetworkLogView._negativeFilter.bind(null, filter);
return filter;
},
/**
* @param {!WebInspector.NetworkLogView.FilterType} type
* @param {string} value
* @param {boolean} negative
* @return {!WebInspector.NetworkLogView.Filter}
*/
_createFilter: function(type, value, negative)
{
var filter = this._createSpecialFilter(type, value);
if (!filter)
return this._createTextFilter((negative ? "-" : "") + type + ":" + value);
if (negative)
return WebInspector.NetworkLogView._negativeFilter.bind(null, filter);
return filter;
},
/**
* @param {!WebInspector.NetworkLogView.FilterType} type
* @param {string} value
* @return {?WebInspector.NetworkLogView.Filter}
*/
_createSpecialFilter: function(type, value)
{
switch (type) {
case WebInspector.NetworkLogView.FilterType.Domain:
return WebInspector.NetworkLogView._createRequestDomainFilter(value);
case WebInspector.NetworkLogView.FilterType.HasResponseHeader:
return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.Is:
if (value.toLowerCase() === WebInspector.NetworkLogView.IsFilterType.Running)
return WebInspector.NetworkLogView._runningRequestFilter;
break;
case WebInspector.NetworkLogView.FilterType.LargerThan:
return this._createSizeFilter(value.toLowerCase());
case WebInspector.NetworkLogView.FilterType.Method:
return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.MimeType:
return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.MixedContent:
return WebInspector.NetworkLogView._requestMixedContentFilter.bind(null, /** @type {!WebInspector.NetworkLogView.MixedContentFilterValues} */ (value));
case WebInspector.NetworkLogView.FilterType.Scheme:
return WebInspector.NetworkLogView._requestSchemeFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.SetCookieDomain:
return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.SetCookieName:
return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.SetCookieValue:
return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
case WebInspector.NetworkLogView.FilterType.StatusCode:
return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
}
return null;
},
/**
* @param {string} value
* @return {?WebInspector.NetworkLogView.Filter}
*/
_createSizeFilter: function(value)
{
var multiplier = 1;
if (value.endsWith("k")) {
multiplier = 1024;
value = value.substring(0, value.length - 1);
} else if (value.endsWith("m")) {
multiplier = 1024 * 1024;
value = value.substring(0, value.length - 1);
}
var quantity = Number(value);
if (isNaN(quantity))
return null;
return WebInspector.NetworkLogView._requestSizeLargerThanFilter.bind(null, quantity * multiplier);
},
_filterRequests: function()
{
this._removeAllHighlights();
this._invalidateAllItems();
},
/**
* @override
*/
jumpToPreviousSearchResult: function()
{
if (!this._matchedRequestCount)
return;
var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
this._highlightNthMatchedRequestForSearch(index, true);
},
/**
* @override
*/
jumpToNextSearchResult: function()
{
if (!this._matchedRequestCount)
return;
var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
this._highlightNthMatchedRequestForSearch(index, true);
},
/**
* @override
*/
searchCanceled: function()
{
delete this._searchRegExp;
this._clearSearchMatchedList();
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
},
/**
* @param {!WebInspector.NetworkRequest} request
*/
revealAndHighlightRequest: function(request)
{
this._removeAllNodeHighlights();
var node = this._nodesByRequestId.get(request.requestId);
if (node) {
node.reveal();
this._highlightNode(node);
}
},
_removeAllNodeHighlights: function()
{
if (this._highlightedNode) {
this._highlightedNode.element().classList.remove("highlighted-row");
delete this._highlightedNode;
}
},
/**
* @param {!WebInspector.NetworkDataGridNode} node
*/
_highlightNode: function(node)
{
WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
this._highlightedNode = node;
},
/**
* @param {!WebInspector.NetworkRequest} request
* @param {string} platform
* @return {string}
*/
_generateCurlCommand: function(request, platform)
{
var command = ["curl"];
// These headers are derived from URL (except "version") and would be added by cURL anyway.
var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
function escapeStringWin(str)
{
/* Replace quote by double quote (but not by \") because it is
recognized by both cmd.exe and MS Crt arguments parser.
Replace % by "%" because it could be expanded to an environment
variable value. So %% becomes "%""%". Even if an env variable ""
(2 doublequotes) is declared, the cmd.exe will not
substitute it with its value.
Replace each backslash with double backslash to make sure
MS Crt arguments parser won't collapse them.
Replace new line outside of quotes since cmd.exe doesn't let
to do it inside.
*/
return "\"" + str.replace(/"/g, "\"\"")
.replace(/%/g, "\"%\"")
.replace(/\\/g, "\\\\")
.replace(/[\r\n]+/g, "\"^$&\"") + "\"";
}
function escapeStringPosix(str)
{
function escapeCharacter(x)
{
var code = x.charCodeAt(0);
if (code < 256) {
// Add leading zero when needed to not care about the next character.
return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
}
code = code.toString(16);
return "\\u" + ("0000" + code).substr(code.length, 4);
}
if (/[^\x20-\x7E]|\'/.test(str)) {
// Use ANSI-C quoting syntax.
return "$\'" + str.replace(/\\/g, "\\\\")
.replace(/\'/g, "\\\'")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
} else {
// Use single quote syntax.
return "'" + str + "'";
}
}
// cURL command expected to run on the same platform that DevTools run
// (it may be different from the inspected page platform).
var escapeString = platform === "win" ? escapeStringWin : escapeStringPosix;
command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
var inferredMethod = "GET";
var data = [];
var requestContentType = request.requestContentType();
if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
data.push("--data");
data.push(escapeString(request.requestFormData));
ignoredHeaders["content-length"] = true;
inferredMethod = "POST";
} else if (request.requestFormData) {
data.push("--data-binary");
data.push(escapeString(request.requestFormData));
ignoredHeaders["content-length"] = true;
inferredMethod = "POST";
}
if (request.requestMethod !== inferredMethod) {
command.push("-X");
command.push(request.requestMethod);
}
var requestHeaders = request.requestHeaders();
for (var i = 0; i < requestHeaders.length; i++) {
var header = requestHeaders[i];
var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
if (name.toLowerCase() in ignoredHeaders)
continue;
command.push("-H");
command.push(escapeString(name + ": " + header.value));
}
command = command.concat(data);
command.push("--compressed");
if (request.securityState() === SecurityAgent.SecurityState.Insecure)
command.push("--insecure");
return command.join(" ");
},
__proto__: WebInspector.VBox.prototype
}
/** @typedef {function(!WebInspector.NetworkRequest): boolean} */
WebInspector.NetworkLogView.Filter;
/**
* @param {!WebInspector.NetworkLogView.Filter} filter
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._negativeFilter = function(filter, request)
{
return !filter(request);
}
/**
* @param {!RegExp} regex
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
{
return regex.test(request.name()) || regex.test(request.path());
}
/**
* @param {string} domain
* @return {!Array.<string>}
*/
WebInspector.NetworkLogView._subdomains = function(domain)
{
var result = [domain];
var indexOfPeriod = domain.indexOf(".");
while (indexOfPeriod !== -1) {
result.push("*" + domain.substring(indexOfPeriod));
indexOfPeriod = domain.indexOf(".", indexOfPeriod + 1);
}
return result;
}
/**
* @param {string} value
* @return {!WebInspector.NetworkLogView.Filter}
*/
WebInspector.NetworkLogView._createRequestDomainFilter = function(value)
{
/**
* @param {string} string
* @return {string}
*/
function escapeForRegExp(string)
{
return string.escapeForRegExp();
}
var escapedPattern = value.split("*").map(escapeForRegExp).join(".*");
return WebInspector.NetworkLogView._requestDomainFilter.bind(null, new RegExp("^" + escapedPattern + "$", "i"));
}
/**
* @param {!RegExp} regex
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestDomainFilter = function(regex, request)
{
return regex.test(request.domain);
}
/**
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._runningRequestFilter = function(request)
{
return !request.finished;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
{
return request.responseHeaderValue(value) !== undefined;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
{
return request.requestMethod === value;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
{
return request.mimeType === value;
}
/**
* @param {!WebInspector.NetworkLogView.MixedContentFilterValues} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestMixedContentFilter = function(value, request)
{
if (value === WebInspector.NetworkLogView.MixedContentFilterValues.Displayed) {
return request.mixedContentType === "optionally-blockable";
} else if (value === WebInspector.NetworkLogView.MixedContentFilterValues.Blocked) {
return request.mixedContentType === "blockable" && request.wasBlocked();
} else if (value === WebInspector.NetworkLogView.MixedContentFilterValues.BlockOverridden) {
return request.mixedContentType === "blockable" && !request.wasBlocked();
} else if (value === WebInspector.NetworkLogView.MixedContentFilterValues.All) {
return request.mixedContentType !== "none";
}
return false;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestSchemeFilter = function(value, request)
{
return request.scheme === value;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
{
var cookies = request.responseCookies;
for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
if (cookies[i].domain() === value)
return true;
}
return false;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
{
var cookies = request.responseCookies;
for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
if (cookies[i].name() === value)
return true;
}
return false;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
{
var cookies = request.responseCookies;
for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
if (cookies[i].value() === value)
return true;
}
return false;
}
/**
* @param {number} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestSizeLargerThanFilter = function(value, request)
{
return request.transferSize >= value;
}
/**
* @param {string} value
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
{
return ("" + request.statusCode) === value;
}
/**
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
{
return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
}
/**
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
{
return request.finished;
}
/**
* @param {number} windowStart
* @param {number} windowEnd
* @param {!WebInspector.NetworkRequest} request
* @return {boolean}
*/
WebInspector.NetworkLogView._requestTimeFilter = function(windowStart, windowEnd, request)
{
if (request.issueTime() > windowEnd)
return false;
if (request.endTime !== -1 && request.endTime < windowStart)
return false;
return true;
}
WebInspector.NetworkLogView.EventTypes = {
RequestSelected: "RequestSelected",
SearchCountUpdated: "SearchCountUpdated",
SearchIndexUpdated: "SearchIndexUpdated",
UpdateRequest: "UpdateRequest"
};