occaecatidicta
Version:
1,292 lines (1,078 loc) • 78.4 kB
JavaScript
/*
* 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
* @extends {WebInspector.View}
*/
WebInspector.NetworkLogView = function()
{
WebInspector.View.call(this);
this.registerRequiredCSS("networkLogView.css");
this._allowResourceSelection = false;
this._resources = [];
this._resourcesById = {};
this._resourcesByURL = {};
this._staleResources = {};
this._resourceGridNodes = {};
this._lastResourceGridNodeId = 0;
this._mainResourceLoadTime = -1;
this._mainResourceDOMContentTime = -1;
this._hiddenCategories = {};
this._matchedResources = [];
this._matchedResourcesMap = {};
this._currentMatchedResourceIndex = -1;
this._categories = WebInspector.resourceCategories;
this._createStatusbarButtons();
this._createFilterStatusBarItems();
this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this);
WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceUpdated, this);
WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoadEventFired, this);
WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
this._initializeView();
function onCanClearBrowserCache(error, result)
{
this._canClearBrowserCache = result;
}
NetworkAgent.canClearBrowserCache(onCanClearBrowserCache.bind(this));
function onCanClearBrowserCookies(error, result)
{
this._canClearBrowserCookies = result;
}
NetworkAgent.canClearBrowserCookies(onCanClearBrowserCookies.bind(this));
}
WebInspector.NetworkLogView.prototype = {
_initializeView: function()
{
this.element.id = "network-container";
this._createSortingFunctions();
this._createTable();
this._createTimelineGrid();
this._createSummaryBar();
if (!this.useLargeRows)
this._setLargerResources(this.useLargeRows);
this._allowPopover = true;
this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
// Enable faster hint.
this._popoverHelper.setTimeout(100);
this.calculator = new WebInspector.NetworkTransferTimeCalculator();
this._filter(this._filterAllElement, false);
this.switchToDetailedView();
},
get statusBarItems()
{
return [this._largerResourcesButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement];
},
get useLargeRows()
{
return WebInspector.settings.resourcesLargeRows.get();
},
set allowPopover(flag)
{
this._allowPopover = flag;
},
get allowResourceSelection()
{
return this._allowResourceSelection;
},
set allowResourceSelection(flag)
{
this._allowResourceSelection = !!flag;
},
elementsToRestoreScrollPositionsFor: function()
{
if (!this._dataGrid) // Not initialized yet.
return [];
return [this._dataGrid.scrollContainer];
},
onResize: function()
{
this._updateOffscreenRows();
},
_createTimelineGrid: function()
{
this._timelineGrid = new WebInspector.TimelineGrid();
this._timelineGrid.element.addStyleClass("network-timeline-grid");
this._dataGrid.element.appendChild(this._timelineGrid.element);
},
_createTable: function()
{
var columns;
if (Capabilities.nativeInstrumentationEnabled)
columns = {name: {}, method: {}, status: {}, type: {}, initiator: {}, size: {}, time: {}, timeline: {}};
else
columns = {name: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}};
columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path"));
columns.name.sortable = true;
columns.name.width = "20%";
columns.name.disclosure = true;
columns.method.title = WebInspector.UIString("Method");
columns.method.sortable = true;
columns.method.width = "6%";
columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text"));
columns.status.sortable = true;
columns.status.width = "6%";
columns.type.title = WebInspector.UIString("Type");
columns.type.sortable = true;
columns.type.width = "6%";
if (Capabilities.nativeInstrumentationEnabled) {
columns.initiator.title = WebInspector.UIString("Initiator");
columns.initiator.sortable = true;
columns.initiator.width = "10%";
}
columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content"));
columns.size.sortable = true;
columns.size.width = "6%";
columns.size.aligned = "right";
columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency"));
columns.time.sortable = true;
columns.time.width = "6%";
columns.time.aligned = "right";
columns.timeline.title = "";
columns.timeline.sortable = false;
if (Capabilities.nativeInstrumentationEnabled)
columns.timeline.width = "40%";
else
columns.timeline.width = "50%";
columns.timeline.sort = "ascending";
this._dataGrid = new WebInspector.DataGrid(columns);
this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
this._dataGrid.element.addStyleClass("network-log-grid");
this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), 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("sorting changed", this._sortItems, this);
this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this);
this._dataGrid.scrollContainer.addEventListener("scroll", this._updateOffscreenRows.bind(this));
this._patchTimelineHeader();
},
_makeHeaderFragment: function(title, subtitle)
{
var fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode(title));
var subtitleDiv = document.createElement("div");
subtitleDiv.className = "network-header-subtitle";
subtitleDiv.textContent = subtitle;
fragment.appendChild(subtitleDiv);
return fragment;
},
_patchTimelineHeader: function()
{
var timelineSorting = document.createElement("select");
var option = document.createElement("option");
option.value = "startTime";
option.label = WebInspector.UIString("Timeline");
timelineSorting.appendChild(option);
option = document.createElement("option");
option.value = "startTime";
option.label = WebInspector.UIString("Start Time");
timelineSorting.appendChild(option);
option = document.createElement("option");
option.value = "responseTime";
option.label = WebInspector.UIString("Response Time");
timelineSorting.appendChild(option);
option = document.createElement("option");
option.value = "endTime";
option.label = WebInspector.UIString("End Time");
timelineSorting.appendChild(option);
option = document.createElement("option");
option.value = "duration";
option.label = WebInspector.UIString("Duration");
timelineSorting.appendChild(option);
option = document.createElement("option");
option.value = "latency";
option.label = WebInspector.UIString("Latency");
timelineSorting.appendChild(option);
var header = this._dataGrid.headerTableHeader("timeline");
header.replaceChild(timelineSorting, header.firstChild);
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.ResourcePropertyComparator.bind(null, "method", false);
this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode", false);
this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType", false);
this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", false);
this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "endTime", false);
this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "responseReceivedTime", false);
this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", true);
this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "latency", true);
var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
this._calculators = {};
this._calculators.timeline = timeCalculator;
this._calculators.startTime = timeCalculator;
this._calculators.endTime = timeCalculator;
this._calculators.responseTime = timeCalculator;
this._calculators.duration = durationCalculator;
this._calculators.latency = durationCalculator;
},
_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.sortOrder === "descending");
this._timelineSortSelector.selectedIndex = 0;
this._updateOffscreenRows();
this.performSearch(null, true);
},
_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;
var sortingFunction = this._sortingFunctions[value];
this._dataGrid.sortNodes(sortingFunction);
this.calculator = this._calculators[value];
if (this.calculator.startAtZero)
this._timelineGrid.hideEventDividers();
else
this._timelineGrid.showEventDividers();
this._dataGrid.markColumnAsSortedBy("timeline", "ascending");
this._updateOffscreenRows();
},
_createFilterStatusBarItems: function()
{
var filterBarElement = document.createElement("div");
filterBarElement.className = "scope-bar status-bar-item";
filterBarElement.id = "network-filter";
function createFilterElement(category, label)
{
var categoryElement = document.createElement("li");
categoryElement.category = category;
categoryElement.className = category;
categoryElement.appendChild(document.createTextNode(label));
categoryElement.addEventListener("click", this._updateFilter.bind(this), false);
filterBarElement.appendChild(categoryElement);
return categoryElement;
}
this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
// Add a divider
var dividerElement = document.createElement("div");
dividerElement.addStyleClass("scope-bar-divider");
filterBarElement.appendChild(dividerElement);
for (var category in this._categories)
createFilterElement.call(this, category, this._categories[category].title);
this._filterBarElement = filterBarElement;
},
_createSummaryBar: function()
{
var tbody = this._dataGrid.dataTableBody;
var tfoot = document.createElement("tfoot");
var tr = tfoot.createChild("tr", "revealed network-summary-bar");
var td = tr.createChild("td");
td.setAttribute("colspan", 7);
tbody.parentNode.insertBefore(tfoot, tbody);
this._summaryBarElement = td;
},
_updateSummaryBar: function()
{
var requestsNumber = this._resources.length;
if (!requestsNumber) {
if (this._summaryBarElement._isDisplayingWarning)
return;
this._summaryBarElement._isDisplayingWarning = true;
var img = document.createElement("img");
img.src = "Images/warningIcon.png";
this._summaryBarElement.removeChildren();
this._summaryBarElement.appendChild(img);
this._summaryBarElement.appendChild(document.createTextNode(
WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
return;
}
delete this._summaryBarElement._isDisplayingWarning;
var transferSize = 0;
var selectedRequestsNumber = 0;
var selectedTransferSize = 0;
var baseTime = -1;
var maxTime = -1;
for (var i = 0; i < this._resources.length; ++i) {
var resource = this._resources[i];
var resourceTransferSize = (resource.cached || !resource.transferSize) ? 0 : resource.transferSize;
transferSize += resourceTransferSize;
if (!this._hiddenCategories.all || !this._hiddenCategories[resource.category.name]) {
selectedRequestsNumber++;
selectedTransferSize += resourceTransferSize;
}
if (resource.url === WebInspector.inspectedPageURL)
baseTime = resource.startTime;
if (resource.endTime > maxTime)
maxTime = resource.endTime;
}
var text = "";
if (this._hiddenCategories.all) {
text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
} else {
text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
}
if (baseTime !== -1 && this._mainResourceLoadTime !== -1 && this._mainResourceDOMContentTime !== -1 && this._mainResourceDOMContentTime > baseTime) {
text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
Number.secondsToString(maxTime - baseTime),
Number.secondsToString(this._mainResourceLoadTime - baseTime),
Number.secondsToString(this._mainResourceDOMContentTime - baseTime));
}
this._summaryBarElement.textContent = text;
},
_showCategory: function(category)
{
this._dataGrid.element.addStyleClass("filter-" + category);
delete this._hiddenCategories[category];
},
_hideCategory: function(category)
{
this._dataGrid.element.removeStyleClass("filter-" + category);
this._hiddenCategories[category] = true;
},
_updateFilter: function(e)
{
this._removeAllNodeHighlights();
var isMac = WebInspector.isMac();
var selectMultiple = false;
if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
selectMultiple = true;
if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
selectMultiple = true;
this._filter(e.target, selectMultiple);
this.performSearch(null, true);
this._updateSummaryBar();
},
_filter: function(target, selectMultiple)
{
function unselectAll()
{
for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) {
var child = this._filterBarElement.childNodes[i];
if (!child.category)
continue;
child.removeStyleClass("selected");
this._hideCategory(child.category);
}
}
if (target.category === this._filterAllElement) {
if (target.hasStyleClass("selected")) {
// We can't unselect All, so we break early here
return;
}
// If All wasn't selected, and now is, unselect everything else.
unselectAll.call(this);
} else {
// Something other than All is being selected, so we want to unselect All.
if (this._filterAllElement.hasStyleClass("selected")) {
this._filterAllElement.removeStyleClass("selected");
this._hideCategory("all");
}
}
if (!selectMultiple) {
// If multiple selection is off, we want to unselect everything else
// and just select ourselves.
unselectAll.call(this);
target.addStyleClass("selected");
this._showCategory(target.category);
this._updateOffscreenRows();
return;
}
if (target.hasStyleClass("selected")) {
// If selectMultiple is turned on, and we were selected, we just
// want to unselect ourselves.
target.removeStyleClass("selected");
this._hideCategory(target.category);
} else {
// If selectMultiple is turned on, and we weren't selected, we just
// want to select ourselves.
target.addStyleClass("selected");
this._showCategory(target.category);
}
this._updateOffscreenRows();
},
_defaultRefreshDelay: 500,
_scheduleRefresh: function()
{
if (this._needsRefresh)
return;
this._needsRefresh = true;
if (this.isShowing() && !this._refreshTimeout)
this._refreshTimeout = setTimeout(this.refresh.bind(this), this._defaultRefreshDelay);
},
_updateDividersIfNeeded: function(force)
{
if (!this._dataGrid)
return;
var timelineColumn = this._dataGrid.columns.timeline;
for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnID) {
// Position timline grid location.
this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
this._timelineGrid.element.style.right = "18px";
}
}
var proceed = true;
if (!this.isShowing()) {
this._scheduleRefresh();
proceed = false;
} else
proceed = this._timelineGrid.updateDividers(force, this.calculator);
if (!proceed)
return;
if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
// If our current sorting method starts at zero, that means it shows all
// resources 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.
// Additionally, if the calculator doesn't have the computePercentageFromEventTime
// function defined, we are probably sorting by size, and event times aren't relevant
// in this case.
return;
}
this._timelineGrid.removeEventDividers();
if (this._mainResourceLoadTime !== -1) {
var percent = this.calculator.computePercentageFromEventTime(this._mainResourceLoadTime);
var loadDivider = document.createElement("div");
loadDivider.className = "network-event-divider network-red-divider";
var loadDividerPadding = document.createElement("div");
loadDividerPadding.className = "network-event-divider-padding";
loadDividerPadding.title = WebInspector.UIString("Load event fired");
loadDividerPadding.appendChild(loadDivider);
loadDividerPadding.style.left = percent + "%";
this._timelineGrid.addEventDivider(loadDividerPadding);
}
if (this._mainResourceDOMContentTime !== -1) {
var percent = this.calculator.computePercentageFromEventTime(this._mainResourceDOMContentTime);
var domContentDivider = document.createElement("div");
domContentDivider.className = "network-event-divider network-blue-divider";
var domContentDividerPadding = document.createElement("div");
domContentDividerPadding.className = "network-event-divider-padding";
domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
domContentDividerPadding.appendChild(domContentDivider);
domContentDividerPadding.style.left = percent + "%";
this._timelineGrid.addEventDivider(domContentDividerPadding);
}
},
_refreshIfNeeded: function()
{
if (this._needsRefresh)
this.refresh();
},
_invalidateAllItems: function()
{
for (var i = 0; i < this._resources.length; ++i) {
var resource = this._resources[i];
this._staleResources[resource.requestId] = resource;
}
},
get calculator()
{
return this._calculator;
},
set calculator(x)
{
if (!x || this._calculator === x)
return;
this._calculator = x;
this._calculator.reset();
this._invalidateAllItems();
this.refresh();
},
_resourceGridNode: function(resource)
{
return this._resourceGridNodes[resource.__gridNodeId];
},
_createResourceGridNode: function(resource)
{
var node = new WebInspector.NetworkDataGridNode(this, resource);
resource.__gridNodeId = this._lastResourceGridNodeId++;
this._resourceGridNodes[resource.__gridNodeId] = node;
return node;
},
_createStatusbarButtons: function()
{
this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked, this);
this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
this._clearButton.addEventListener("click", this._reset, this);
this._largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
this._largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows.get();
this._largerResourcesButton.addEventListener("click", this._toggleLargerResources, this);
},
_onLoadEventFired: function(event)
{
this._mainResourceLoadTime = event.data || -1;
// Schedule refresh to update boundaries and draw the new line.
this._scheduleRefresh();
},
_domContentLoadedEventFired: function(event)
{
this._mainResourceDOMContentTime = event.data || -1;
// Schedule refresh to update boundaries and draw the new line.
this._scheduleRefresh();
},
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 wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
var boundariesChanged = false;
if (this.calculator.updateBoundariesForEventTime) {
boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainResourceLoadTime) || boundariesChanged;
boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainResourceDOMContentTime) || boundariesChanged;
}
for (var resourceId in this._staleResources) {
var resource = this._staleResources[resourceId];
var node = this._resourceGridNode(resource);
if (!node) {
// Create the timeline tree element and graph.
node = this._createResourceGridNode(resource);
this._dataGrid.appendChild(node);
}
node.refreshResource();
if (this.calculator.updateBoundaries(resource))
boundariesChanged = true;
if (!node.isFilteredOut())
this._updateHighlightIfMatched(resource);
}
if (boundariesChanged) {
// The boundaries changed, so all item graphs are stale.
this._invalidateAllItems();
}
for (var resourceId in this._staleResources)
this._resourceGridNode(this._staleResources[resourceId]).refreshGraph(this.calculator);
this._staleResources = {};
this._sortItems();
this._updateSummaryBar();
this._dataGrid.updateWidths();
// FIXME: evaluate performance impact of moving this before a call to sortItems()
if (wasScrolledToLastRow)
this._dataGrid.scrollToLastRow();
},
_onPreserveLogClicked: function(e)
{
this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
},
_reset: function()
{
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
this._clearSearchMatchedList();
if (this._popoverHelper)
this._popoverHelper.hidePopover();
if (this._calculator)
this._calculator.reset();
this._resources = [];
this._resourcesById = {};
this._resourcesByURL = {};
this._staleResources = {};
this._resourceGridNodes = {};
if (this._dataGrid) {
this._dataGrid.removeChildren();
this._updateDividersIfNeeded(true);
this._updateSummaryBar();
}
this._mainResourceLoadTime = -1;
this._mainResourceDOMContentTime = -1;
this._linkifier.reset();
},
get resources()
{
return this._resources;
},
resourceById: function(id)
{
return this._resourcesById[id];
},
_onResourceStarted: function(event)
{
this._appendResource(event.data);
},
_appendResource: function(resource)
{
this._resources.push(resource);
// In case of redirect request id is reassigned to a redirected
// resource and we need to update _resourcesById ans search results.
if (this._resourcesById[resource.requestId]) {
var oldResource = resource.redirects[resource.redirects.length - 1];
this._resourcesById[oldResource.requestId] = oldResource;
this._updateSearchMatchedListAfterRequestIdChanged(resource.requestId, oldResource.requestId);
}
this._resourcesById[resource.requestId] = resource;
this._resourcesByURL[resource.url] = resource;
// Pull all the redirects of the main resource upon commit load.
if (resource.redirects) {
for (var i = 0; i < resource.redirects.length; ++i)
this._refreshResource(resource.redirects[i]);
}
this._refreshResource(resource);
},
_onResourceUpdated: function(event)
{
this._refreshResource(event.data);
},
_refreshResource: function(resource)
{
this._staleResources[resource.requestId] = resource;
this._scheduleRefresh();
},
clear: function()
{
if (this._preserveLogToggle.toggled)
return;
this._reset();
},
_mainFrameNavigated: function(event)
{
if (this._preserveLogToggle.toggled)
return;
var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data;
var loaderId = frame.loaderId;
// Preserve provisional load resources.
var resourcesToPreserve = [];
for (var i = 0; i < this._resources.length; ++i) {
var resource = this._resources[i];
if (resource.loaderId === loaderId)
resourcesToPreserve.push(resource);
}
this._reset();
// Restore preserved items.
for (var i = 0; i < resourcesToPreserve.length; ++i)
this._appendResource(resourcesToPreserve[i]);
},
switchToDetailedView: function()
{
if (!this._dataGrid)
return;
if (this._dataGrid.selectedNode)
this._dataGrid.selectedNode.selected = false;
this.element.removeStyleClass("brief-mode");
this._dataGrid.showColumn("method");
this._dataGrid.showColumn("status");
this._dataGrid.showColumn("type");
if (Capabilities.nativeInstrumentationEnabled)
this._dataGrid.showColumn("initiator");
this._dataGrid.showColumn("size");
this._dataGrid.showColumn("time");
this._dataGrid.showColumn("timeline");
var widths = {};
widths.name = 20;
widths.method = 6;
widths.status = 6;
widths.type = 6;
if (Capabilities.nativeInstrumentationEnabled)
widths.initiator = 10;
widths.size = 6;
widths.time = 6;
if (Capabilities.nativeInstrumentationEnabled)
widths.timeline = 40;
else
widths.timeline = 50;
this._dataGrid.applyColumnWidthsMap(widths);
},
switchToBriefView: function()
{
this.element.addStyleClass("brief-mode");
this._removeAllNodeHighlights();
this._dataGrid.hideColumn("method");
this._dataGrid.hideColumn("status");
this._dataGrid.hideColumn("type");
if (Capabilities.nativeInstrumentationEnabled)
this._dataGrid.hideColumn("initiator");
this._dataGrid.hideColumn("size");
this._dataGrid.hideColumn("time");
this._dataGrid.hideColumn("timeline");
var widths = {};
widths.name = 100;
this._dataGrid.applyColumnWidthsMap(widths);
this._popoverHelper.hidePopover();
},
_toggleLargerResources: function()
{
WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
this._setLargerResources(WebInspector.settings.resourcesLargeRows.get());
},
_setLargerResources: function(enabled)
{
this._largerResourcesButton.toggled = enabled;
if (!enabled) {
this._largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
this._dataGrid.element.addStyleClass("small");
this._timelineGrid.element.addStyleClass("small");
} else {
this._largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
this._dataGrid.element.removeStyleClass("small");
this._timelineGrid.element.removeStyleClass("small");
}
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
this._updateOffscreenRows();
},
_getPopoverAnchor: function(element)
{
if (!this._allowPopover)
return;
var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
if (!anchor)
return null;
var resource = anchor.parentElement.resource;
return resource && resource.timing ? anchor : null;
},
/**
* @param {Element} anchor
* @param {WebInspector.Popover} popover
*/
_showPopover: function(anchor, popover)
{
var resource = anchor.parentElement.resource;
var tableElement = WebInspector.ResourceTimingView.createTimingTable(resource);
popover.show(tableElement, anchor);
},
_contextMenu: function(event)
{
var contextMenu = new WebInspector.ContextMenu();
var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
var resource = gridNode && gridNode._resource;
if (resource) {
contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, resource.url, false));
contextMenu.appendSeparator();
contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, resource));
if (resource.requestHeadersText)
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, resource));
if (resource.responseHeadersText)
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, resource));
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy entry as HAR" : "Copy Entry as HAR"), this._copyResource.bind(this, resource));
}
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
if (InspectorFrontendHost.canSaveAs()) {
contextMenu.appendSeparator();
if (resource)
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save entry as HAR" : "Save Entry as HAR"), this._exportResource.bind(this, resource));
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save all as HAR" : "Save All as HAR"), this._exportAll.bind(this));
}
if (this._canClearBrowserCache || this._canClearBrowserCookies)
contextMenu.appendSeparator();
if (this._canClearBrowserCache)
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
if (this._canClearBrowserCookies)
contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
contextMenu.show(event);
},
_copyAll: function()
{
var harArchive = {
log: (new WebInspector.HARLog(this._resources)).build()
};
InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
},
_copyResource: function(resource)
{
var har = (new WebInspector.HAREntry(resource)).build();
InspectorFrontendHost.copyText(JSON.stringify(har, null, 2));
},
_copyLocation: function(resource)
{
InspectorFrontendHost.copyText(resource.url);
},
_copyRequestHeaders: function(resource)
{
InspectorFrontendHost.copyText(resource.requestHeadersText);
},
_copyResponseHeaders: function(resource)
{
InspectorFrontendHost.copyText(resource.responseHeadersText);
},
_exportAll: function()
{
var harArchive = {
log: (new WebInspector.HARLog(this._resources)).build()
};
InspectorFrontendHost.saveAs(WebInspector.inspectedPageDomain + ".har", JSON.stringify(harArchive, null, 2));
},
_exportResource: function(resource)
{
var har = (new WebInspector.HAREntry(resource)).build();
InspectorFrontendHost.saveAs(resource.displayName + ".har", JSON.stringify(har, null, 2));
},
_clearBrowserCache: function(event)
{
if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
NetworkAgent.clearBrowserCache();
},
_clearBrowserCookies: function(event)
{
if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
NetworkAgent.clearBrowserCookies();
},
_updateOffscreenRows: function()
{
var dataTableBody = this._dataGrid.dataTableBody;
var rows = dataTableBody.children;
var recordsCount = rows.length;
if (recordsCount < 2)
return; // Filler row only.
var visibleTop = this._dataGrid.scrollContainer.scrollTop;
var visibleBottom = visibleTop + this._dataGrid.scrollContainer.offsetHeight;
var rowHeight = 0;
// Filler is at recordsCount - 1.
var unfilteredRowIndex = 0;
for (var i = 0; i < recordsCount - 1; ++i) {
var row = rows[i];
var dataGridNode = this._dataGrid.dataGridNodeFromNode(row);
if (dataGridNode.isFilteredOut()) {
row.removeStyleClass("offscreen");
continue;
}
if (!rowHeight)
rowHeight = row.offsetHeight;
var rowIsVisible = unfilteredRowIndex * rowHeight < visibleBottom && (unfilteredRowIndex + 1) * rowHeight > visibleTop;
if (rowIsVisible !== row.rowIsVisible) {
if (rowIsVisible)
row.removeStyleClass("offscreen");
else
row.addStyleClass("offscreen");
row.rowIsVisible = rowIsVisible;
}
unfilteredRowIndex++;
}
},
_matchResource: function(resource)
{
if (!this._searchRegExp)
return -1;
if ((!resource.displayName || !resource.displayName.match(this._searchRegExp)) && !resource.folder.match(this._searchRegExp))
return -1;
if (resource.requestId in this._matchedResourcesMap)
return this._matchedResourcesMap[resource.requestId];
var matchedResourceIndex = this._matchedResources.length;
this._matchedResourcesMap[resource.requestId] = matchedResourceIndex;
this._matchedResources.push(resource.requestId);
return matchedResourceIndex;
},
_clearSearchMatchedList: function()
{
this._matchedResources = [];
this._matchedResourcesMap = {};
this._highlightNthMatchedResource(-1, false);
},
_updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
{
var resourceIndex = this._matchedResourcesMap[oldRequestId];
if (resourceIndex) {
delete this._matchedResourcesMap[oldRequestId];
this._matchedResourcesMap[newRequestId] = resourceIndex;
this._matchedResources[resourceIndex] = newRequestId;
}
},
_updateHighlightIfMatched: function(resource)
{
var matchedResourceIndex = this._matchResource(resource);
if (matchedResourceIndex === -1)
return;
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedResources.length);
if (this._currentMatchedResourceIndex !== -1 && this._currentMatchedResourceIndex !== matchedResourceIndex)
return;
this._highlightNthMatchedResource(matchedResourceIndex, false);
},
_highlightNthMatchedResource: function(matchedResourceIndex, reveal)
{
if (this._highlightedSubstringChanges) {
revertDomChanges(this._highlightedSubstringChanges);
this._highlightedSubstringChanges = null;
}
if (matchedResourceIndex === -1) {
this._currentMatchedResourceIndex = matchedResourceIndex;
return;
}
var resource = this._resourcesById[this._matchedResources[matchedResourceIndex]];
if (!resource)
return;
var nameMatched = resource.displayName && resource.displayName.match(this._searchRegExp);
var pathMatched = resource.path && resource.folder.match(this._searchRegExp);
if (!nameMatched && pathMatched && !this._largerResourcesButton.toggled)
this._toggleLargerResources();
var node = this._resourceGridNode(resource);
if (node) {
this._highlightedSubstringChanges = node._highlightMatchedSubstring(this._searchRegExp);
if (reveal)
node.reveal();
this._currentMatchedResourceIndex = matchedResourceIndex;
}
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedResourceIndex);
},
performSearch: function(searchQuery, sortOrFilterApplied)
{
var newMatchedResourceIndex = 0;
var currentMatchedRequestId;
if (this._currentMatchedResourceIndex !== -1)
currentMatchedRequestId = this._matchedResources[this._currentMatchedResourceIndex];
if (!sortOrFilterApplied)
this._searchRegExp = createPlainTextSearchRegex(searchQuery, "i");
this._clearSearchMatchedList();
var childNodes = this._dataGrid.dataTableBody.childNodes;
var resourceNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
for (var i = 0; i < resourceNodes.length; ++i) {
var dataGridNode = this._dataGrid.dataGridNodeFromNode(resourceNodes[i]);
if (dataGridNode.isFilteredOut())
continue;
if (this._matchResource(dataGridNode._resource) !== -1 && dataGridNode._resource.requestId === currentMatchedRequestId)
newMatchedResourceIndex = this._matchedResources.length - 1;
}
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedResources.length);
this._highlightNthMatchedResource(newMatchedResourceIndex, !sortOrFilterApplied);
},
jumpToPreviousSearchResult: function()
{
if (!this._matchedResources.length)
return;
this._highlightNthMatchedResource((this._currentMatchedResourceIndex + this._matchedResources.length - 1) % this._matchedResources.length, true);
},
jumpToNextSearchResult: function()
{
if (!this._matchedResources.length)
return;
this._highlightNthMatchedResource((this._currentMatchedResourceIndex + 1) % this._matchedResources.length, true);
},
searchCanceled: function()
{
this._clearSearchMatchedList();
this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
},
revealAndHighlightResource: function(resource)
{
this._removeAllNodeHighlights();
var node = this._resourceGridNode(resource);
if (node) {
this._dataGrid.element.focus();
node.reveal();
this._highlightNode(node);
}
},
_removeAllNodeHighlights: function()
{
if (this._highlightedNode) {
this._highlightedNode.element.removeStyleClass("highlighted-row");
delete this._highlightedNode;
}
},
_highlightNode: function(node)
{
node.element.addStyleClass("highlighted-row");
this._highlightedNode = node;
}
};
WebInspector.NetworkLogView.prototype.__proto__ = WebInspector.View.prototype;
WebInspector.NetworkLogView.EventTypes = {
ViewCleared: "ViewCleared",
RowSizeChanged: "RowSizeChanged",
ResourceSelected: "ResourceSelected",
SearchCountUpdated: "SearchCountUpdated",
SearchIndexUpdated: "SearchIndexUpdated"
};
/**
* @constructor
* @extends {WebInspector.Panel}
*/
WebInspector.NetworkPanel = function()
{
WebInspector.Panel.call(this, "network");
this.registerRequiredCSS("networkPanel.css");
this.createSplitView();
this.splitView.hideMainElement();
this._networkLogView = new WebInspector.NetworkLogView();
this._networkLogView.show(this.sidebarElement);
this._viewsContainerElement = this.splitView.mainElement;
this._viewsContainerElement.id = "network-views";
this._viewsContainerElement.addStyleClass("hidden");
if (!this._networkLogView.useLargeRows)
this._viewsContainerElement.addStyleClass("small");
this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ResourceSelected, this._onResourceSelected, this);
this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
this._closeButtonElement = document.createElement("button");
this._closeButtonElement.id = "network-close-button";
this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
this._viewsContainerElement.appendChild(this._closeButtonElement);
function viewGetter()
{
return this.visibleView;
}
WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
}
WebInspector.NetworkPanel.prototype = {
get toolbarItemLabel()
{
return WebInspector.UIString("Network");
},
get statusBarItems()
{
return this._networkLogView.statusBarItems;
},
elementsToRestoreScrollPositionsFor: function()
{
return this._networkLogView.elementsToRestoreScrollPositionsFor();
},
// FIXME: only used by the layout tests, should not be exposed.
_reset: function()
{
this._networkLogView._reset();
},
handleShortcut: function(event)
{
if (this._viewingResourceMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
this._toggleGridMode();
event.handled = true;