UNPKG

occaecatidicta

Version:
1,292 lines (1,078 loc) 78.4 kB
/* * 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;