2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 2011 Google Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 importScript("RequestView.js");
32 importScript("NetworkItemView.js");
33 importScript("RequestCookiesView.js");
34 importScript("RequestHeadersView.js");
35 importScript("RequestHTMLView.js");
36 importScript("RequestJSONView.js");
37 importScript("RequestPreviewView.js");
38 importScript("RequestResponseView.js");
39 importScript("RequestTimingView.js");
40 importScript("ResourceWebSocketFrameView.js");
44 * @extends {WebInspector.View}
45 * @param {WebInspector.Setting} coulmnsVisibilitySetting
47 WebInspector.NetworkLogView = function(coulmnsVisibilitySetting)
49 WebInspector.View.call(this);
50 this.registerRequiredCSS("networkLogView.css");
52 this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
53 this._allowRequestSelection = false;
55 this._requestsById = {};
56 this._requestsByURL = {};
57 this._staleRequests = {};
58 this._requestGridNodes = {};
59 this._lastRequestGridNodeId = 0;
60 this._mainRequestLoadTime = -1;
61 this._mainRequestDOMContentTime = -1;
62 this._hiddenCategories = {};
63 this._matchedRequests = [];
64 this._highlightedSubstringChanges = [];
65 this._filteredOutRequests = new Map();
67 this._matchedRequestsMap = {};
68 this._currentMatchedRequestIndex = -1;
70 this._createStatusbarButtons();
71 this._createStatusBarItems();
72 this._linkifier = new WebInspector.Linkifier();
74 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
75 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
76 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
78 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
79 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoadEventFired, this);
80 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
82 this._initializeView();
83 function onCanClearBrowserCache(error, result)
85 this._canClearBrowserCache = result;
87 NetworkAgent.canClearBrowserCache(onCanClearBrowserCache.bind(this));
89 function onCanClearBrowserCookies(error, result)
91 this._canClearBrowserCookies = result;
93 NetworkAgent.canClearBrowserCookies(onCanClearBrowserCookies.bind(this));
95 WebInspector.networkLog.requests.forEach(this._appendRequest.bind(this));
98 WebInspector.NetworkLogView._defaultColumnsVisivility = {method: true, status: true, domain: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true};
100 WebInspector.NetworkLogView.prototype = {
101 _initializeView: function()
103 this.element.id = "network-container";
105 this._createSortingFunctions();
107 this._createTimelineGrid();
108 this._createSummaryBar();
110 if (!this.useLargeRows)
111 this._setLargerRequests(this.useLargeRows);
113 this._allowPopover = true;
114 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
115 // Enable faster hint.
116 this._popoverHelper.setTimeout(100);
118 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
119 this._filter(this._filterAllElement, false);
121 this.switchToDetailedView();
126 return [this._largerRequestsButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement, this._progressBarContainer];
131 return WebInspector.settings.resourcesLargeRows.get();
134 set allowPopover(flag)
136 this._allowPopover = flag;
139 elementsToRestoreScrollPositionsFor: function()
141 if (!this._dataGrid) // Not initialized yet.
143 return [this._dataGrid.scrollContainer];
148 this._updateOffscreenRows();
151 _createTimelineGrid: function()
153 this._timelineGrid = new WebInspector.TimelineGrid();
154 this._timelineGrid.element.addStyleClass("network-timeline-grid");
155 this._dataGrid.element.appendChild(this._timelineGrid.element);
158 _createTable: function()
160 var columns = {name: {}, method: {}, status: {}, domain: {}, type: {}, initiator: {}, cookies: {}, setCookies: {}, size: {}, time: {}, timeline: {}};
162 columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path"));
163 columns.name.name = WebInspector.UIString("Name");
164 columns.name.sortable = true;
165 columns.name.weight = 20;
166 columns.name.disclosure = true;
168 columns.method.title = WebInspector.UIString("Method");
169 columns.method.sortable = true;
170 columns.method.weight = 6;
172 columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text"));
173 columns.status.name = WebInspector.UIString("Status");
174 columns.status.sortable = true;
175 columns.status.weight = 6;
177 columns.domain.title = WebInspector.UIString("Domain");
178 columns.domain.sortable = true;
179 columns.domain.weight = 6;
181 columns.type.title = WebInspector.UIString("Type");
182 columns.type.sortable = true;
183 columns.type.weight = 6;
185 columns.initiator.title = WebInspector.UIString("Initiator");
186 columns.initiator.sortable = true;
187 columns.initiator.weight = 10;
189 columns.cookies.title = WebInspector.UIString("Cookies");
190 columns.cookies.sortable = true;
191 columns.cookies.weight = 6;
192 columns.cookies.aligned = "right";
194 columns.setCookies.title = WebInspector.UIString("Set-Cookies");
195 columns.setCookies.sortable = true;
196 columns.setCookies.weight = 6;
197 columns.setCookies.aligned = "right";
199 columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content"));
200 columns.size.name = WebInspector.UIString("Size");
201 columns.size.sortable = true;
202 columns.size.weight = 6;
203 columns.size.aligned = "right";
205 columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency"));
206 columns.time.name = WebInspector.UIString("Time");
207 columns.time.sortable = true;
208 columns.time.weight = 6;
209 columns.time.aligned = "right";
211 columns.timeline.title = "";
212 columns.timeline.name = WebInspector.UIString("Timeline");
213 columns.timeline.sortable = false;
214 columns.timeline.weight = 40;
215 columns.timeline.sort = "ascending";
217 this._dataGrid = new WebInspector.DataGrid(columns);
218 this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
219 this._dataGrid.element.addStyleClass("network-log-grid");
220 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
221 this._dataGrid.show(this.element);
223 // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
224 this._dataGrid.addEventListener("sorting changed", this._sortItems, this);
225 this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this);
226 this._dataGrid.scrollContainer.addEventListener("scroll", this._updateOffscreenRows.bind(this));
228 this._patchTimelineHeader();
231 _makeHeaderFragment: function(title, subtitle)
233 var fragment = document.createDocumentFragment();
234 fragment.createTextChild(title);
235 var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
236 subtitleDiv.createTextChild(subtitle);
240 _patchTimelineHeader: function()
242 var timelineSorting = document.createElement("select");
244 var option = document.createElement("option");
245 option.value = "startTime";
246 option.label = WebInspector.UIString("Timeline");
247 timelineSorting.appendChild(option);
249 option = document.createElement("option");
250 option.value = "startTime";
251 option.label = WebInspector.UIString("Start Time");
252 timelineSorting.appendChild(option);
254 option = document.createElement("option");
255 option.value = "responseTime";
256 option.label = WebInspector.UIString("Response Time");
257 timelineSorting.appendChild(option);
259 option = document.createElement("option");
260 option.value = "endTime";
261 option.label = WebInspector.UIString("End Time");
262 timelineSorting.appendChild(option);
264 option = document.createElement("option");
265 option.value = "duration";
266 option.label = WebInspector.UIString("Duration");
267 timelineSorting.appendChild(option);
269 option = document.createElement("option");
270 option.value = "latency";
271 option.label = WebInspector.UIString("Latency");
272 timelineSorting.appendChild(option);
274 var header = this._dataGrid.headerTableHeader("timeline");
275 header.replaceChild(timelineSorting, header.firstChild);
277 timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
278 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
279 this._timelineSortSelector = timelineSorting;
282 _createSortingFunctions: function()
284 this._sortingFunctions = {};
285 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
286 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
287 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
288 this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
289 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
290 this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
291 this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
292 this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
293 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
294 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
295 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
296 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
297 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
298 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
299 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
300 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
302 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
303 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
305 this._calculators = {};
306 this._calculators.timeline = timeCalculator;
307 this._calculators.startTime = timeCalculator;
308 this._calculators.endTime = timeCalculator;
309 this._calculators.responseTime = timeCalculator;
310 this._calculators.duration = durationCalculator;
311 this._calculators.latency = durationCalculator;
314 _sortItems: function()
316 this._removeAllNodeHighlights();
317 var columnIdentifier = this._dataGrid.sortColumnIdentifier;
318 if (columnIdentifier === "timeline") {
319 this._sortByTimeline();
322 var sortingFunction = this._sortingFunctions[columnIdentifier];
323 if (!sortingFunction)
326 this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending");
327 this._timelineSortSelector.selectedIndex = 0;
328 this._updateOffscreenRows();
330 this.searchCanceled();
332 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
333 action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
334 column: columnIdentifier,
335 sortOrder: this._dataGrid.sortOrder
339 _sortByTimeline: function()
341 this._removeAllNodeHighlights();
342 var selectedIndex = this._timelineSortSelector.selectedIndex;
344 selectedIndex = 1; // Sort by start time by default.
345 var selectedOption = this._timelineSortSelector[selectedIndex];
346 var value = selectedOption.value;
348 var sortingFunction = this._sortingFunctions[value];
349 this._dataGrid.sortNodes(sortingFunction);
350 this.calculator = this._calculators[value];
351 if (this.calculator.startAtZero)
352 this._timelineGrid.hideEventDividers();
354 this._timelineGrid.showEventDividers();
355 this._dataGrid.markColumnAsSortedBy("timeline", "ascending");
356 this._updateOffscreenRows();
359 _createStatusBarItems: function()
361 var filterBarElement = document.createElement("div");
362 filterBarElement.className = "scope-bar status-bar-item";
363 filterBarElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta));
366 * @param {string} typeName
367 * @param {string} label
369 function createFilterElement(typeName, label)
371 var categoryElement = document.createElement("li");
372 categoryElement.typeName = typeName;
373 categoryElement.className = typeName;
374 categoryElement.createTextChild(label);
375 categoryElement.addEventListener("click", this._updateFilter.bind(this), false);
376 filterBarElement.appendChild(categoryElement);
378 return categoryElement;
381 this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
382 filterBarElement.createChild("div", "scope-bar-divider");
384 for (var typeId in WebInspector.resourceTypes) {
385 var type = WebInspector.resourceTypes[typeId];
386 createFilterElement.call(this, type.name(), type.categoryTitle());
388 this._filterBarElement = filterBarElement;
389 this._progressBarContainer = document.createElement("div");
390 this._progressBarContainer.className = "status-bar-item";
393 _createSummaryBar: function()
395 var tbody = this._dataGrid.dataTableBody;
396 var tfoot = document.createElement("tfoot");
397 var tr = tfoot.createChild("tr", "revealed network-summary-bar");
398 var td = tr.createChild("td");
399 td.setAttribute("colspan", 7);
400 tbody.parentNode.insertBefore(tfoot, tbody);
401 this._summaryBarElement = td;
404 _updateSummaryBar: function()
406 var requestsNumber = this._requests.length;
408 if (!requestsNumber) {
409 if (this._summaryBarElement._isDisplayingWarning)
411 this._summaryBarElement._isDisplayingWarning = true;
413 var img = document.createElement("img");
414 img.src = "Images/warningIcon.png";
415 this._summaryBarElement.removeChildren();
416 this._summaryBarElement.appendChild(img);
417 this._summaryBarElement.appendChild(document.createTextNode(
418 WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
421 delete this._summaryBarElement._isDisplayingWarning;
423 var transferSize = 0;
424 var selectedRequestsNumber = 0;
425 var selectedTransferSize = 0;
428 for (var i = 0; i < this._requests.length; ++i) {
429 var request = this._requests[i];
430 var requestTransferSize = (request.cached || !request.transferSize) ? 0 : request.transferSize;
431 transferSize += requestTransferSize;
432 if ((!this._hiddenCategories["all"] || !this._hiddenCategories[request.type.name()]) && !this._filteredOutRequests.get(request)) {
433 selectedRequestsNumber++;
434 selectedTransferSize += requestTransferSize;
436 if (request.url === WebInspector.inspectedPageURL)
437 baseTime = request.startTime;
438 if (request.endTime > maxTime)
439 maxTime = request.endTime;
442 if (selectedRequestsNumber !== requestsNumber) {
443 text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
444 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
446 text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
447 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
449 if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentTime !== -1 && this._mainRequestDOMContentTime > baseTime) {
450 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
451 Number.secondsToString(maxTime - baseTime),
452 Number.secondsToString(this._mainRequestLoadTime - baseTime),
453 Number.secondsToString(this._mainRequestDOMContentTime - baseTime));
455 this._summaryBarElement.textContent = text;
458 _showCategory: function(typeName)
460 this._dataGrid.element.addStyleClass("filter-" + typeName);
461 delete this._hiddenCategories[typeName];
464 _hideCategory: function(typeName)
466 this._dataGrid.element.removeStyleClass("filter-" + typeName);
467 this._hiddenCategories[typeName] = true;
470 _updateFilter: function(e)
472 this._removeAllNodeHighlights();
473 var isMac = WebInspector.isMac();
474 var selectMultiple = false;
475 if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
476 selectMultiple = true;
477 if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
478 selectMultiple = true;
480 this._filter(e.target, selectMultiple);
481 this.searchCanceled();
482 this._updateSummaryBar();
485 _filter: function(target, selectMultiple)
487 function unselectAll()
489 for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) {
490 var child = this._filterBarElement.childNodes[i];
494 child.removeStyleClass("selected");
495 this._hideCategory(child.typeName);
499 if (target === this._filterAllElement) {
500 if (target.hasStyleClass("selected")) {
501 // We can't unselect All, so we break early here
505 // If All wasn't selected, and now is, unselect everything else.
506 unselectAll.call(this);
508 // Something other than All is being selected, so we want to unselect All.
509 if (this._filterAllElement.hasStyleClass("selected")) {
510 this._filterAllElement.removeStyleClass("selected");
511 this._hideCategory("all");
515 if (!selectMultiple) {
516 // If multiple selection is off, we want to unselect everything else
517 // and just select ourselves.
518 unselectAll.call(this);
520 target.addStyleClass("selected");
521 this._showCategory(target.typeName);
522 this._updateOffscreenRows();
526 if (target.hasStyleClass("selected")) {
527 // If selectMultiple is turned on, and we were selected, we just
528 // want to unselect ourselves.
529 target.removeStyleClass("selected");
530 this._hideCategory(target.typeName);
532 // If selectMultiple is turned on, and we weren't selected, we just
533 // want to select ourselves.
534 target.addStyleClass("selected");
535 this._showCategory(target.typeName);
537 this._updateOffscreenRows();
540 _defaultRefreshDelay: 500,
542 _scheduleRefresh: function()
544 if (this._needsRefresh)
547 this._needsRefresh = true;
549 if (this.isShowing() && !this._refreshTimeout)
550 this._refreshTimeout = setTimeout(this.refresh.bind(this), this._defaultRefreshDelay);
553 _updateDividersIfNeeded: function()
557 var timelineColumn = this._dataGrid.columns.timeline;
558 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
559 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnIndex) {
560 // Position timline grid location.
561 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
562 this._timelineGrid.element.style.right = "18px";
567 if (!this.isShowing()) {
568 this._scheduleRefresh();
571 this.calculator.setDisplayWindow(this._timelineGrid.element.clientWidth);
572 proceed = this._timelineGrid.updateDividers(this.calculator);
577 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
578 // If our current sorting method starts at zero, that means it shows all
579 // requests starting at the same point, and so onLoad event and DOMContent
580 // event lines really wouldn't make much sense here, so don't render them.
581 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
582 // function defined, we are probably sorting by size, and event times aren't relevant
587 this._timelineGrid.removeEventDividers();
588 if (this._mainRequestLoadTime !== -1) {
589 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
591 var loadDivider = document.createElement("div");
592 loadDivider.className = "network-event-divider network-red-divider";
594 var loadDividerPadding = document.createElement("div");
595 loadDividerPadding.className = "network-event-divider-padding";
596 loadDividerPadding.title = WebInspector.UIString("Load event fired");
597 loadDividerPadding.appendChild(loadDivider);
598 loadDividerPadding.style.left = percent + "%";
599 this._timelineGrid.addEventDivider(loadDividerPadding);
602 if (this._mainRequestDOMContentTime !== -1) {
603 var percent = this.calculator.computePercentageFromEventTime(this._mainRequestDOMContentTime);
605 var domContentDivider = document.createElement("div");
606 domContentDivider.className = "network-event-divider network-blue-divider";
608 var domContentDividerPadding = document.createElement("div");
609 domContentDividerPadding.className = "network-event-divider-padding";
610 domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
611 domContentDividerPadding.appendChild(domContentDivider);
612 domContentDividerPadding.style.left = percent + "%";
613 this._timelineGrid.addEventDivider(domContentDividerPadding);
617 _refreshIfNeeded: function()
619 if (this._needsRefresh)
623 _invalidateAllItems: function()
625 for (var i = 0; i < this._requests.length; ++i) {
626 var request = this._requests[i];
627 this._staleRequests[request.requestId] = request;
633 return this._calculator;
638 if (!x || this._calculator === x)
641 this._calculator = x;
642 this._calculator.reset();
644 this._invalidateAllItems();
648 _requestGridNode: function(request)
650 return this._requestGridNodes[request.__gridNodeId];
653 _createRequestGridNode: function(request)
655 var node = new WebInspector.NetworkDataGridNode(this, request);
656 request.__gridNodeId = this._lastRequestGridNodeId++;
657 this._requestGridNodes[request.__gridNodeId] = node;
661 _createStatusbarButtons: function()
663 this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
664 this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked, this);
666 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
667 this._clearButton.addEventListener("click", this._reset, this);
669 this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
670 this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
671 this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
674 _onLoadEventFired: function(event)
676 this._mainRequestLoadTime = event.data || -1;
677 // Schedule refresh to update boundaries and draw the new line.
678 this._scheduleRefresh();
681 _domContentLoadedEventFired: function(event)
683 this._mainRequestDOMContentTime = event.data || -1;
684 // Schedule refresh to update boundaries and draw the new line.
685 this._scheduleRefresh();
690 this._refreshIfNeeded();
695 this._popoverHelper.hidePopover();
700 this._needsRefresh = false;
701 if (this._refreshTimeout) {
702 clearTimeout(this._refreshTimeout);
703 delete this._refreshTimeout;
706 this._removeAllNodeHighlights();
707 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
708 var boundariesChanged = false;
709 if (this.calculator.updateBoundariesForEventTime) {
710 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
711 boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestDOMContentTime) || boundariesChanged;
714 for (var requestId in this._staleRequests) {
715 var request = this._staleRequests[requestId];
716 var node = this._requestGridNode(request);
718 node.refreshRequest();
720 // Create the timeline tree element and graph.
721 node = this._createRequestGridNode(request);
722 this._dataGrid.rootNode().appendChild(node);
723 node.refreshRequest();
724 this._applyFilter(node);
727 if (this.calculator.updateBoundaries(request))
728 boundariesChanged = true;
730 if (!node.isFilteredOut())
731 this._updateHighlightIfMatched(request);
734 if (boundariesChanged) {
735 // The boundaries changed, so all item graphs are stale.
736 this._invalidateAllItems();
739 for (var requestId in this._staleRequests)
740 this._requestGridNode(this._staleRequests[requestId]).refreshGraph(this.calculator);
742 this._staleRequests = {};
744 this._updateSummaryBar();
745 this._dataGrid.updateWidths();
746 // FIXME: evaluate performance impact of moving this before a call to sortItems()
747 if (wasScrolledToLastRow)
748 this._dataGrid.scrollToLastRow();
751 _onPreserveLogClicked: function(e)
753 this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
758 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
760 this._clearSearchMatchedList();
761 if (this._popoverHelper)
762 this._popoverHelper.hidePopover();
764 if (this._calculator)
765 this._calculator.reset();
768 this._requestsById = {};
769 this._requestsByURL = {};
770 this._staleRequests = {};
771 this._requestGridNodes = {};
773 if (this._dataGrid) {
774 this._dataGrid.rootNode().removeChildren();
775 this._updateDividersIfNeeded();
776 this._updateSummaryBar();
779 this._mainRequestLoadTime = -1;
780 this._mainRequestDOMContentTime = -1;
781 this._linkifier.reset();
786 return this._requests;
789 requestById: function(id)
791 return this._requestsById[id];
794 _onRequestStarted: function(event)
796 this._appendRequest(event.data);
799 _appendRequest: function(request)
801 this._requests.push(request);
803 // In case of redirect request id is reassigned to a redirected
804 // request and we need to update _requestsById ans search results.
805 if (this._requestsById[request.requestId]) {
806 var oldRequest = request.redirects[request.redirects.length - 1];
807 this._requestsById[oldRequest.requestId] = oldRequest;
809 this._updateSearchMatchedListAfterRequestIdChanged(request.requestId, oldRequest.requestId);
811 this._requestsById[request.requestId] = request;
813 this._requestsByURL[request.url] = request;
815 // Pull all the redirects of the main request upon commit load.
816 if (request.redirects) {
817 for (var i = 0; i < request.redirects.length; ++i)
818 this._refreshRequest(request.redirects[i]);
821 this._refreshRequest(request);
825 * @param {WebInspector.Event} event
827 _onRequestUpdated: function(event)
829 var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
830 this._refreshRequest(request);
834 * @param {WebInspector.NetworkRequest} request
836 _refreshRequest: function(request)
838 this._staleRequests[request.requestId] = request;
839 this._scheduleRefresh();
844 if (this._preserveLogToggle.toggled)
849 _mainFrameNavigated: function(event)
851 if (this._preserveLogToggle.toggled)
854 var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data);
855 var loaderId = frame.loaderId;
857 // Preserve provisional load requests.
858 var requestsToPreserve = [];
859 for (var i = 0; i < this._requests.length; ++i) {
860 var request = this._requests[i];
861 if (request.loaderId === loaderId)
862 requestsToPreserve.push(request);
867 // Restore preserved items.
868 for (var i = 0; i < requestsToPreserve.length; ++i)
869 this._appendRequest(requestsToPreserve[i]);
872 switchToDetailedView: function()
876 if (this._dataGrid.selectedNode)
877 this._dataGrid.selectedNode.selected = false;
879 this.element.removeStyleClass("brief-mode");
880 this._detailedMode = true;
881 this._updateColumns();
884 switchToBriefView: function()
886 this.element.addStyleClass("brief-mode");
887 this._removeAllNodeHighlights();
888 this._detailedMode = false;
889 this._updateColumns();
890 this._popoverHelper.hidePopover();
893 _toggleLargerRequests: function()
895 WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
896 this._setLargerRequests(WebInspector.settings.resourcesLargeRows.get());
899 _setLargerRequests: function(enabled)
901 this._largerRequestsButton.toggled = enabled;
903 this._largerRequestsButton.title = WebInspector.UIString("Use large resource rows.");
904 this._dataGrid.element.addStyleClass("small");
905 this._timelineGrid.element.addStyleClass("small");
907 this._largerRequestsButton.title = WebInspector.UIString("Use small resource rows.");
908 this._dataGrid.element.removeStyleClass("small");
909 this._timelineGrid.element.removeStyleClass("small");
911 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
912 this._updateOffscreenRows();
915 _getPopoverAnchor: function(element)
917 if (!this._allowPopover)
919 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
920 if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
922 anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
923 if (anchor && anchor.request && anchor.request.initiator)
930 * @param {Element} anchor
931 * @param {WebInspector.Popover} popover
933 _showPopover: function(anchor, popover)
936 if (anchor.hasStyleClass("network-script-initiated"))
937 content = this._generateScriptInitiatedPopoverContent(anchor.request);
939 content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
940 popover.show(content, anchor);
944 * @param {!WebInspector.NetworkRequest} request
947 _generateScriptInitiatedPopoverContent: function(request)
949 var stackTrace = request.initiator.stackTrace;
950 var framesTable = document.createElement("table");
951 for (var i = 0; i < stackTrace.length; ++i) {
952 var stackFrame = stackTrace[i];
953 var row = document.createElement("tr");
954 row.createChild("td").textContent = stackFrame.functionName ? stackFrame.functionName : WebInspector.UIString("(anonymous function)");
955 row.createChild("td").textContent = " @ ";
956 row.createChild("td").appendChild(this._linkifier.linkifyLocation(stackFrame.url, stackFrame.lineNumber - 1, 0));
957 framesTable.appendChild(row);
962 _updateColumns: function()
964 var columnsVisibility = this._coulmnsVisibilitySetting.get();
965 var detailedMode = !!this._detailedMode;
966 for (var columnIdentifier in columnsVisibility) {
967 var visible = detailedMode && columnsVisibility[columnIdentifier];
968 this._dataGrid.setColumnVisible(columnIdentifier, visible);
970 this._dataGrid.setColumnVisible("timeline", detailedMode);
971 this._dataGrid.applyColumnWeights();
975 * @param {string} columnIdentifier
977 _toggleColumnVisibility: function(columnIdentifier)
979 var columnsVisibility = this._coulmnsVisibilitySetting.get();
980 columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
981 this._coulmnsVisibilitySetting.set(columnsVisibility);
983 this._updateColumns();
986 _contextMenu: function(event)
988 var contextMenu = new WebInspector.ContextMenu(event);
990 if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
991 var columnsVisibility = this._coulmnsVisibilitySetting.get();
992 for (var columnIdentifier in columnsVisibility) {
993 var column = this._dataGrid.columns[columnIdentifier];
994 contextMenu.appendCheckboxItem(column.name || column.title, this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1000 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1001 var request = gridNode && gridNode._request;
1004 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, request.url, false));
1005 contextMenu.appendSeparator();
1006 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1007 if (request.requestHeadersText)
1008 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1009 if (request.responseHeadersText)
1010 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1011 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy as curl" : "Copy as Curl"), this._copyCurlCommand.bind(this, request));
1013 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1015 if (InspectorFrontendHost.canSave()) {
1016 contextMenu.appendSeparator();
1017 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1020 if (this._canClearBrowserCache || this._canClearBrowserCookies)
1021 contextMenu.appendSeparator();
1022 if (this._canClearBrowserCache)
1023 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1024 if (this._canClearBrowserCookies)
1025 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1028 if (request && request.type === WebInspector.resourceTypes.XHR) {
1029 contextMenu.appendSeparator();
1030 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
1031 contextMenu.appendSeparator();
1037 _replayXHR: function(requestId)
1039 NetworkAgent.replayXHR(requestId);
1042 _copyAll: function()
1045 log: (new WebInspector.HARLog(this._requests)).build()
1047 InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1050 _copyLocation: function(request)
1052 InspectorFrontendHost.copyText(request.url);
1055 _copyRequestHeaders: function(request)
1057 InspectorFrontendHost.copyText(request.requestHeadersText);
1060 _copyResponseHeaders: function(request)
1062 InspectorFrontendHost.copyText(request.responseHeadersText);
1066 * @param {WebInspector.NetworkRequest} request
1068 _copyCurlCommand: function(request)
1070 InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1073 _exportAll: function()
1075 var filename = WebInspector.inspectedPageDomain + ".har";
1076 var stream = new WebInspector.FileOutputStream();
1077 stream.open(filename, openCallback.bind(this));
1078 function openCallback()
1080 var progressIndicator = new WebInspector.ProgressIndicator();
1081 this._progressBarContainer.appendChild(progressIndicator.element);
1082 var harWriter = new WebInspector.HARWriter();
1083 harWriter.write(stream, this._requests, progressIndicator);
1087 _clearBrowserCache: function()
1089 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1090 NetworkAgent.clearBrowserCache();
1093 _clearBrowserCookies: function()
1095 if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1096 NetworkAgent.clearBrowserCookies();
1099 _updateOffscreenRows: function()
1101 var dataTableBody = this._dataGrid.dataTableBody;
1102 var rows = dataTableBody.children;
1103 var recordsCount = rows.length;
1104 if (recordsCount < 2)
1105 return; // Filler row only.
1107 var visibleTop = this._dataGrid.scrollContainer.scrollTop;
1108 var visibleBottom = visibleTop + this._dataGrid.scrollContainer.offsetHeight;
1112 // Filler is at recordsCount - 1.
1113 var unfilteredRowIndex = 0;
1114 for (var i = 0; i < recordsCount - 1; ++i) {
1117 var dataGridNode = this._dataGrid.dataGridNodeFromNode(row);
1118 if (dataGridNode.isFilteredOut()) {
1119 row.removeStyleClass("offscreen");
1124 rowHeight = row.offsetHeight;
1126 var rowIsVisible = unfilteredRowIndex * rowHeight < visibleBottom && (unfilteredRowIndex + 1) * rowHeight > visibleTop;
1127 if (rowIsVisible !== row.rowIsVisible) {
1128 row.enableStyleClass("offscreen", !rowIsVisible);
1129 row.rowIsVisible = rowIsVisible;
1131 unfilteredRowIndex++;
1135 _matchRequest: function(request)
1137 if (!this._searchRegExp)
1140 if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp))
1143 if (request.requestId in this._matchedRequestsMap)
1144 return this._matchedRequestsMap[request.requestId];
1146 var matchedRequestIndex = this._matchedRequests.length;
1147 this._matchedRequestsMap[request.requestId] = matchedRequestIndex;
1148 this._matchedRequests.push(request.requestId);
1150 return matchedRequestIndex;
1153 _clearSearchMatchedList: function()
1155 delete this._searchRegExp;
1156 this._matchedRequests = [];
1157 this._matchedRequestsMap = {};
1158 this._removeAllHighlights();
1161 _updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
1163 var requestIndex = this._matchedRequestsMap[oldRequestId];
1165 delete this._matchedRequestsMap[oldRequestId];
1166 this._matchedRequestsMap[newRequestId] = requestIndex;
1167 this._matchedRequests[requestIndex] = newRequestId;
1171 _updateHighlightIfMatched: function(request)
1173 var matchedRequestIndex = this._matchRequest(request);
1174 if (matchedRequestIndex === -1)
1177 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1179 if (this._currentMatchedRequestIndex !== -1 && this._currentMatchedRequestIndex !== matchedRequestIndex)
1182 this._highlightNthMatchedRequestForSearch(matchedRequestIndex, false);
1185 _removeAllHighlights: function()
1187 for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1188 WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1189 this._highlightedSubstringChanges = [];
1193 * @param {WebInspector.NetworkRequest} request
1194 * @param {boolean} reveal
1195 * @param {RegExp=} regExp
1197 _highlightMatchedRequest: function(request, reveal, regExp)
1199 var node = this._requestGridNode(request);
1203 var nameMatched = request.name().match(regExp);
1204 var pathMatched = request.path().match(regExp);
1205 if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1206 this._toggleLargerRequests();
1207 var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
1208 this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1214 * @param {number} matchedRequestIndex
1215 * @param {boolean} reveal
1217 _highlightNthMatchedRequestForSearch: function(matchedRequestIndex, reveal)
1219 var request = this.requestById(this._matchedRequests[matchedRequestIndex]);
1222 this._removeAllHighlights();
1223 this._highlightMatchedRequest(request, reveal, this._searchRegExp);
1224 var node = this._requestGridNode(request);
1226 this._currentMatchedRequestIndex = matchedRequestIndex;
1228 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedRequestIndex);
1231 performSearch: function(searchQuery)
1233 var newMatchedRequestIndex = 0;
1234 var currentMatchedRequestId;
1235 if (this._currentMatchedRequestIndex !== -1)
1236 currentMatchedRequestId = this._matchedRequests[this._currentMatchedRequestIndex];
1238 this._clearSearchMatchedList();
1239 this._searchRegExp = createPlainTextSearchRegex(searchQuery, "i");
1241 var childNodes = this._dataGrid.dataTableBody.childNodes;
1242 var requestNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
1244 for (var i = 0; i < requestNodes.length; ++i) {
1245 var dataGridNode = this._dataGrid.dataGridNodeFromNode(requestNodes[i]);
1246 if (dataGridNode.isFilteredOut())
1248 if (this._matchRequest(dataGridNode._request) !== -1 && dataGridNode._request.requestId === currentMatchedRequestId)
1249 newMatchedRequestIndex = this._matchedRequests.length - 1;
1252 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1253 this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, false);
1257 * @param {!WebInspector.NetworkDataGridNode} node
1259 _applyFilter: function(node) {
1260 var filter = this._filterRegExp;
1261 var request = node._request;
1264 if (filter.test(request.name()) || filter.test(request.path()))
1265 this._highlightMatchedRequest(request, false, filter);
1267 node.element.addStyleClass("filtered-out");
1268 this._filteredOutRequests.put(request, true);
1273 * @param {string} query
1275 performFilter: function(query)
1277 this._removeAllHighlights();
1278 this._filteredOutRequests.clear();
1279 delete this._filterRegExp;
1281 this._filterRegExp = createPlainTextSearchRegex(query, "i");
1283 var nodes = this._dataGrid.rootNode().children;
1284 for (var i = 0; i < nodes.length; ++i) {
1285 nodes[i].element.removeStyleClass("filtered-out");
1286 this._applyFilter(nodes[i]);
1288 this._updateSummaryBar();
1289 this._updateOffscreenRows();
1292 jumpToPreviousSearchResult: function()
1294 if (!this._matchedRequests.length)
1296 this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + this._matchedRequests.length - 1) % this._matchedRequests.length, true);
1299 jumpToNextSearchResult: function()
1301 if (!this._matchedRequests.length)
1303 this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + 1) % this._matchedRequests.length, true);
1306 searchCanceled: function()
1308 this._clearSearchMatchedList();
1309 this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1312 revealAndHighlightRequest: function(request)
1314 this._removeAllNodeHighlights();
1316 var node = this._requestGridNode(request);
1318 this._dataGrid.element.focus();
1320 this._highlightNode(node);
1324 _removeAllNodeHighlights: function()
1326 if (this._highlightedNode) {
1327 this._highlightedNode.element.removeStyleClass("highlighted-row");
1328 delete this._highlightedNode;
1332 _highlightNode: function(node)
1334 node.element.addStyleClass("highlighted-row");
1335 this._highlightedNode = node;
1339 * @param {WebInspector.NetworkRequest} request
1342 _generateCurlCommand: function(request)
1344 var command = ["curl"];
1345 var ignoredHeaders = {};
1347 function escape(str)
1349 return "\"" + str.replace(/\\/g, "\\\\")
1350 .replace(/\"/g, "\\\"")
1351 .replace(/\$/g, "\\$")
1352 .replace(/\n/g, "\\\n")
1353 .replace(/\`/g, "\\\`") + "\"";
1355 command.push(escape(request.url));
1357 var inferredMethod = "GET";
1359 var requestContentType = request.requestContentType();
1360 if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1361 data.push("--data");
1362 data.push(escape(request.requestFormData));
1363 ignoredHeaders["Content-Length"] = true;
1364 inferredMethod = "POST";
1365 } else if (request.requestFormData) {
1366 data.push("--data-binary");
1367 data.push(escape(request.requestFormData));
1368 ignoredHeaders["Content-Length"] = true;
1369 inferredMethod = "POST";
1372 if (request.requestMethod !== inferredMethod) {
1374 command.push(request.requestMethod);
1377 for (var i = 0; i < request.requestHeaders.length; i++) {
1378 var header = request.requestHeaders[i];
1379 if (header.name in ignoredHeaders)
1382 command.push(escape(header.name + ": " + header.value));
1384 command = command.concat(data);
1385 return command.join(" ");
1388 __proto__: WebInspector.View.prototype
1392 WebInspector.NetworkLogView.EventTypes = {
1393 ViewCleared: "ViewCleared",
1394 RowSizeChanged: "RowSizeChanged",
1395 RequestSelected: "RequestSelected",
1396 SearchCountUpdated: "SearchCountUpdated",
1397 SearchIndexUpdated: "SearchIndexUpdated"
1402 * @extends {WebInspector.Panel}
1403 * @implements {WebInspector.ContextMenu.Provider}
1405 WebInspector.NetworkPanel = function()
1407 WebInspector.Panel.call(this, "network");
1408 this.registerRequiredCSS("networkPanel.css");
1409 this._injectStyles();
1411 this.createSidebarView();
1412 this.splitView.hideMainElement();
1414 var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisivility;
1415 var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1416 var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1417 var columnsVisibility = {};
1418 for (var columnId in defaultColumnsVisibility)
1419 columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1420 networkLogColumnsVisibilitySetting.set(columnsVisibility);
1422 this._networkLogView = new WebInspector.NetworkLogView(networkLogColumnsVisibilitySetting);
1423 this._networkLogView.show(this.sidebarElement);
1425 this._viewsContainerElement = this.splitView.mainElement;
1426 this._viewsContainerElement.id = "network-views";
1427 this._viewsContainerElement.addStyleClass("hidden");
1428 if (!this._networkLogView.useLargeRows)
1429 this._viewsContainerElement.addStyleClass("small");
1431 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1432 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1433 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1434 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1435 this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1437 this._closeButtonElement = document.createElement("button");
1438 this._closeButtonElement.id = "network-close-button";
1439 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1440 this._viewsContainerElement.appendChild(this._closeButtonElement);
1442 function viewGetter()
1444 return this.visibleView;
1446 WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
1449 WebInspector.NetworkPanel.prototype = {
1450 get statusBarItems()
1452 return this._networkLogView.statusBarItems;
1455 elementsToRestoreScrollPositionsFor: function()
1457 return this._networkLogView.elementsToRestoreScrollPositionsFor();
1460 // FIXME: only used by the layout tests, should not be exposed.
1463 this._networkLogView._reset();
1466 handleShortcut: function(event)
1468 if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1469 this._toggleGridMode();
1470 event.handled = true;
1474 WebInspector.Panel.prototype.handleShortcut.call(this, event);
1477 wasShown: function()
1479 WebInspector.Panel.prototype.wasShown.call(this);
1484 return this._networkLogView.requests;
1487 requestById: function(id)
1489 return this._networkLogView.requestById(id);
1492 _requestByAnchor: function(anchor)
1494 return anchor.requestId ? this.requestById(anchor.requestId) : this._networkLogView._requestsByURL[anchor.href];
1497 canShowAnchorLocation: function(anchor)
1499 return !!this._requestByAnchor(anchor);
1502 showAnchorLocation: function(anchor)
1504 var request = this._requestByAnchor(anchor);
1505 this.revealAndHighlightRequest(request)
1508 revealAndHighlightRequest: function(request)
1510 this._toggleGridMode();
1512 this._networkLogView.revealAndHighlightRequest(request);
1515 _onViewCleared: function(event)
1517 this._closeVisibleRequest();
1518 this._toggleGridMode();
1519 this._viewsContainerElement.removeChildren();
1520 this._viewsContainerElement.appendChild(this._closeButtonElement);
1523 _onRowSizeChanged: function(event)
1525 this._viewsContainerElement.enableStyleClass("small", !event.data.largeRows);
1528 _onSearchCountUpdated: function(event)
1530 WebInspector.searchController.updateSearchMatchesCount(event.data, this);
1533 _onSearchIndexUpdated: function(event)
1535 WebInspector.searchController.updateCurrentMatchIndex(event.data, this);
1538 _onRequestSelected: function(event)
1540 this._showRequest(event.data);
1543 _showRequest: function(request)
1548 this._toggleViewingRequestMode();
1550 if (this.visibleView) {
1551 this.visibleView.detach();
1552 delete this.visibleView;
1555 var view = new WebInspector.NetworkItemView(request);
1556 view.show(this._viewsContainerElement);
1557 this.visibleView = view;
1560 _closeVisibleRequest: function()
1562 this.element.removeStyleClass("viewing-resource");
1564 if (this.visibleView) {
1565 this.visibleView.detach();
1566 delete this.visibleView;
1570 _toggleGridMode: function()
1572 if (this._viewingRequestMode) {
1573 this._viewingRequestMode = false;
1574 this.element.removeStyleClass("viewing-resource");
1575 this.splitView.hideMainElement();
1578 this._networkLogView.switchToDetailedView();
1579 this._networkLogView.allowPopover = true;
1580 this._networkLogView._allowRequestSelection = false;
1583 _toggleViewingRequestMode: function()
1585 if (this._viewingRequestMode)
1587 this._viewingRequestMode = true;
1589 this.element.addStyleClass("viewing-resource");
1590 this.splitView.showMainElement();
1591 this._networkLogView.allowPopover = false;
1592 this._networkLogView._allowRequestSelection = true;
1593 this._networkLogView.switchToBriefView();
1597 * @param {string} searchQuery
1599 performSearch: function(searchQuery)
1601 this._networkLogView.performSearch(searchQuery);
1607 canFilter: function()
1613 * @param {string} query
1615 performFilter: function(query)
1617 this._networkLogView.performFilter(query);
1620 jumpToPreviousSearchResult: function()
1622 this._networkLogView.jumpToPreviousSearchResult();
1625 jumpToNextSearchResult: function()
1627 this._networkLogView.jumpToNextSearchResult();
1630 searchCanceled: function()
1632 this._networkLogView.searchCanceled();
1636 * @param {WebInspector.ContextMenu} contextMenu
1637 * @param {Object} target
1639 appendApplicableItems: function(event, contextMenu, target)
1641 if (!(target instanceof WebInspector.NetworkRequest))
1643 if (this.visibleView && this.visibleView.isShowing() && this.visibleView.request() === target)
1648 WebInspector.inspectorView.setCurrentPanel(this);
1649 this.revealAndHighlightRequest(/** @type {WebInspector.NetworkRequest} */ (target));
1651 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in network panel" : "Reveal in Network Panel"), reveal.bind(this));
1654 _injectStyles: function()
1656 var style = document.createElement("style");
1659 var columns = WebInspector.NetworkLogView._defaultColumnsVisivility;
1661 var hideSelectors = [];
1662 var bgSelectors = [];
1663 for (var columnId in columns) {
1664 hideSelectors.push("#network-container .hide-" + columnId + "-column ." + columnId + "-column");
1665 bgSelectors.push(".network-log-grid.data-grid td." + columnId + "-column");
1667 rules.push(hideSelectors.join(", ") + "{border-right: 0 none transparent;}");
1668 rules.push(bgSelectors.join(", ") + "{background-color: rgba(0, 0, 0, 0.07);}");
1670 var filterSelectors = [];
1671 for (var typeId in WebInspector.resourceTypes) {
1672 var typeName = WebInspector.resourceTypes[typeId].name();
1673 filterSelectors.push(".network-log-grid.data-grid.filter-" + typeName + " table.data tr.revealed.network-type-" + typeName + ":not(.filtered-out)");
1675 filterSelectors.push(".network-log-grid.data-grid.filter-all table.data tr.revealed.network-item:not(.filtered-out)");
1676 rules.push(filterSelectors.join(", ") + "{display: table-row;}");
1678 style.textContent = rules.join("\n");
1679 document.head.appendChild(style);
1682 __proto__: WebInspector.Panel.prototype
1687 * @implements {WebInspector.TimelineGrid.Calculator}
1689 WebInspector.NetworkBaseCalculator = function()
1693 WebInspector.NetworkBaseCalculator.prototype = {
1694 computePosition: function(time)
1696 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
1699 computeBarGraphPercentages: function(item)
1701 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
1704 computeBarGraphLabels: function(item)
1706 const label = this.formatTime(this._value(item));
1707 return {left: label, right: label, tooltip: label};
1710 boundarySpan: function()
1712 return this._maximumBoundary - this._minimumBoundary;
1715 updateBoundaries: function(item)
1717 this._minimumBoundary = 0;
1719 var value = this._value(item);
1720 if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
1721 this._maximumBoundary = value;
1729 delete this._minimumBoundary;
1730 delete this._maximumBoundary;
1733 maximumBoundary: function()
1735 return this._maximumBoundary;
1738 minimumBoundary: function()
1740 return this._minimumBoundary;
1743 _value: function(item)
1748 formatTime: function(value)
1750 return value.toString();
1753 setDisplayWindow: function(clientWidth)
1755 this._workingArea = clientWidth;
1756 this.paddingLeft = 0;
1762 * @extends {WebInspector.NetworkBaseCalculator}
1764 WebInspector.NetworkTimeCalculator = function(startAtZero)
1766 WebInspector.NetworkBaseCalculator.call(this);
1767 this.startAtZero = startAtZero;
1770 WebInspector.NetworkTimeCalculator.prototype = {
1771 computeBarGraphPercentages: function(request)
1773 if (request.startTime !== -1)
1774 var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1778 if (request.responseReceivedTime !== -1)
1779 var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1781 var middle = (this.startAtZero ? start : 100);
1783 if (request.endTime !== -1)
1784 var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1786 var end = (this.startAtZero ? middle : 100);
1788 if (this.startAtZero) {
1794 return {start: start, middle: middle, end: end};
1797 computePercentageFromEventTime: function(eventTime)
1799 // This function computes a percentage in terms of the total loading time
1800 // of a specific event. If startAtZero is set, then this is useless, and we
1801 // want to return 0.
1802 if (eventTime !== -1 && !this.startAtZero)
1803 return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
1808 updateBoundariesForEventTime: function(eventTime)
1810 if (eventTime === -1 || this.startAtZero)
1813 if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
1814 this._maximumBoundary = eventTime;
1820 computeBarGraphLabels: function(request)
1822 var rightLabel = "";
1823 if (request.responseReceivedTime !== -1 && request.endTime !== -1)
1824 rightLabel = this.formatTime(request.endTime - request.responseReceivedTime);
1826 var hasLatency = request.latency > 0;
1828 var leftLabel = this.formatTime(request.latency);
1830 var leftLabel = rightLabel;
1833 return {left: leftLabel, right: rightLabel};
1835 if (hasLatency && rightLabel) {
1836 var total = this.formatTime(request.duration);
1837 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
1838 } else if (hasLatency)
1839 var tooltip = WebInspector.UIString("%s latency", leftLabel);
1840 else if (rightLabel)
1841 var tooltip = WebInspector.UIString("%s download", rightLabel);
1844 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
1845 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
1848 updateBoundaries: function(request)
1850 var didChange = false;
1853 if (this.startAtZero)
1856 lowerBound = this._lowerBound(request);
1858 if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
1859 this._minimumBoundary = lowerBound;
1863 var upperBound = this._upperBound(request);
1864 if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
1865 this._maximumBoundary = upperBound;
1872 formatTime: function(value)
1874 return Number.secondsToString(value);
1877 _lowerBound: function(request)
1882 _upperBound: function(request)
1887 __proto__: WebInspector.NetworkBaseCalculator.prototype
1892 * @extends {WebInspector.NetworkTimeCalculator}
1894 WebInspector.NetworkTransferTimeCalculator = function()
1896 WebInspector.NetworkTimeCalculator.call(this, false);
1899 WebInspector.NetworkTransferTimeCalculator.prototype = {
1900 formatTime: function(value)
1902 return Number.secondsToString(value);
1905 _lowerBound: function(request)
1907 return request.startTime;
1910 _upperBound: function(request)
1912 return request.endTime;
1915 __proto__: WebInspector.NetworkTimeCalculator.prototype
1920 * @extends {WebInspector.NetworkTimeCalculator}
1922 WebInspector.NetworkTransferDurationCalculator = function()
1924 WebInspector.NetworkTimeCalculator.call(this, true);
1927 WebInspector.NetworkTransferDurationCalculator.prototype = {
1928 formatTime: function(value)
1930 return Number.secondsToString(value);
1933 _upperBound: function(request)
1935 return request.duration;
1938 __proto__: WebInspector.NetworkTimeCalculator.prototype
1943 * @extends {WebInspector.DataGridNode}
1944 * @param {!WebInspector.NetworkLogView} parentView
1945 * @param {!WebInspector.NetworkRequest} request
1947 WebInspector.NetworkDataGridNode = function(parentView, request)
1949 WebInspector.DataGridNode.call(this, {});
1950 this._parentView = parentView;
1951 this._request = request;
1954 WebInspector.NetworkDataGridNode.prototype = {
1955 createCells: function()
1957 // Out of sight, out of mind: create nodes offscreen to save on render tree update times when running updateOffscreenRows()
1958 this._element.addStyleClass("offscreen");
1959 this._nameCell = this._createDivInTD("name");
1960 this._methodCell = this._createDivInTD("method");
1961 this._statusCell = this._createDivInTD("status");
1962 this._domainCell = this._createDivInTD("domain");
1963 this._typeCell = this._createDivInTD("type");
1964 this._initiatorCell = this._createDivInTD("initiator");
1965 this._cookiesCell = this._createDivInTD("cookies");
1966 this._setCookiesCell = this._createDivInTD("setCookies");
1967 this._sizeCell = this._createDivInTD("size");
1968 this._timeCell = this._createDivInTD("time");
1969 this._createTimelineCell();
1970 this._nameCell.addEventListener("click", this._onClick.bind(this), false);
1971 this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
1974 isFilteredOut: function()
1976 if (this._parentView._filteredOutRequests.get(this._request))
1978 if (!this._parentView._hiddenCategories["all"])
1980 return this._request.type.name() in this._parentView._hiddenCategories;
1983 _onClick: function()
1985 if (!this._parentView._allowRequestSelection)
1991 this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
1992 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
1994 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
1995 action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
1996 url: this._request.url
2000 _highlightMatchedSubstring: function(regexp)
2002 var domChanges = [];
2003 var matchInfo = this._element.textContent.match(regexp);
2005 WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2009 _openInNewTab: function()
2011 InspectorFrontendHost.openInNewTab(this._request.url);
2016 return this._parentView._allowRequestSelection && !this.isFilteredOut();
2019 _createDivInTD: function(columnIdentifier)
2021 var td = this.createTD(columnIdentifier);
2022 var div = td.createChild("div");
2023 this._element.appendChild(td);
2027 _createTimelineCell: function()
2029 this._graphElement = document.createElement("div");
2030 this._graphElement.className = "network-graph-side";
2032 this._barAreaElement = document.createElement("div");
2033 // this._barAreaElement.className = "network-graph-bar-area hidden";
2034 this._barAreaElement.className = "network-graph-bar-area";
2035 this._barAreaElement.request = this._request;
2036 this._graphElement.appendChild(this._barAreaElement);
2038 this._barLeftElement = document.createElement("div");
2039 this._barLeftElement.className = "network-graph-bar waiting";
2040 this._barAreaElement.appendChild(this._barLeftElement);
2042 this._barRightElement = document.createElement("div");
2043 this._barRightElement.className = "network-graph-bar";
2044 this._barAreaElement.appendChild(this._barRightElement);
2047 this._labelLeftElement = document.createElement("div");
2048 this._labelLeftElement.className = "network-graph-label waiting";
2049 this._barAreaElement.appendChild(this._labelLeftElement);
2051 this._labelRightElement = document.createElement("div");
2052 this._labelRightElement.className = "network-graph-label";
2053 this._barAreaElement.appendChild(this._labelRightElement);
2055 this._graphElement.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2057 this._timelineCell = document.createElement("td");
2058 this._timelineCell.className = "timeline-column";
2059 this._element.appendChild(this._timelineCell);
2060 this._timelineCell.appendChild(this._graphElement);
2063 refreshRequest: function()
2065 this._refreshNameCell();
2067 this._methodCell.setTextAndTitle(this._request.requestMethod);
2069 this._refreshStatusCell();
2070 this._refreshDomainCell();
2071 this._refreshTypeCell();
2072 this._refreshInitiatorCell();
2073 this._refreshCookiesCell();
2074 this._refreshSetCookiesCell();
2075 this._refreshSizeCell();
2076 this._refreshTimeCell();
2078 if (this._request.cached)
2079 this._graphElement.addStyleClass("resource-cached");
2081 this._element.addStyleClass("network-item");
2082 this._updateElementStyleClasses(this._element);
2086 * @param {!Element} element
2088 _updateElementStyleClasses: function(element)
2090 var typeClassName = "network-type-" + this._request.type.name();
2091 if (!element.hasStyleClass(typeClassName)) {
2092 element.removeMatchingStyleClasses("network-type-\\w+");
2093 element.addStyleClass(typeClassName);
2097 _refreshNameCell: function()
2099 this._nameCell.removeChildren();
2101 if (this._request.type === WebInspector.resourceTypes.Image) {
2102 var previewImage = document.createElement("img");
2103 previewImage.className = "image-network-icon-preview";
2104 this._request.populateImageSource(previewImage);
2106 var iconElement = document.createElement("div");
2107 iconElement.className = "icon";
2108 iconElement.appendChild(previewImage);
2110 var iconElement = document.createElement("img");
2111 iconElement.className = "icon";
2113 this._nameCell.appendChild(iconElement);
2114 this._nameCell.appendChild(document.createTextNode(this._request.name()));
2115 this._appendSubtitle(this._nameCell, this._request.path());
2116 this._nameCell.title = this._request.url;
2119 _refreshStatusCell: function()
2121 this._statusCell.removeChildren();
2123 if (this._request.failed) {
2124 var failText = this._request.canceled ? WebInspector.UIString("(canceled)") : WebInspector.UIString("(failed)");
2125 if (this._request.localizedFailDescription) {
2126 this._statusCell.appendChild(document.createTextNode(failText));
2127 this._appendSubtitle(this._statusCell, this._request.localizedFailDescription);
2128 this._statusCell.title = failText + " " + this._request.localizedFailDescription;
2130 this._statusCell.setTextAndTitle(failText);
2132 this._statusCell.addStyleClass("network-dim-cell");
2133 this.element.addStyleClass("network-error-row");
2137 this._statusCell.removeStyleClass("network-dim-cell");
2138 this.element.removeStyleClass("network-error-row");
2140 if (this._request.statusCode) {
2141 this._statusCell.appendChild(document.createTextNode("" + this._request.statusCode));
2142 this._appendSubtitle(this._statusCell, this._request.statusText);
2143 this._statusCell.title = this._request.statusCode + " " + this._request.statusText;
2144 if (this._request.statusCode >= 400)
2145 this.element.addStyleClass("network-error-row");
2146 if (this._request.cached)
2147 this._statusCell.addStyleClass("network-dim-cell");
2149 if (!this._request.isHttpFamily() && this._request.finished)
2150 this._statusCell.setTextAndTitle(WebInspector.UIString("Success"));
2151 else if (this._request.isPingRequest())
2152 this._statusCell.setTextAndTitle(WebInspector.UIString("(ping)"));
2154 this._statusCell.setTextAndTitle(WebInspector.UIString("(pending)"));
2155 this._statusCell.addStyleClass("network-dim-cell");
2159 _refreshDomainCell: function()
2161 this._domainCell.removeChildren();
2162 this._domainCell.appendChild(document.createTextNode(this._request.domain));
2163 this._domainCell.title = this._request.parsedURL.host;
2166 _refreshTypeCell: function()
2168 if (this._request.mimeType) {
2169 this._typeCell.removeStyleClass("network-dim-cell");
2170 this._typeCell.setTextAndTitle(this._request.mimeType);
2171 } else if (this._request.isPingRequest()) {
2172 this._typeCell.removeStyleClass("network-dim-cell");
2173 this._typeCell.setTextAndTitle(this._request.requestContentType());
2175 this._typeCell.addStyleClass("network-dim-cell");
2176 this._typeCell.setTextAndTitle(WebInspector.UIString("Pending"));
2180 _refreshInitiatorCell: function()
2182 this._initiatorCell.removeStyleClass("network-dim-cell");
2183 this._initiatorCell.removeStyleClass("network-script-initiated");
2184 delete this._initiatorCell.request;
2185 this._initiatorCell.title = "";
2187 var initiator = this._request.initiator;
2188 if ((initiator && initiator.type !== "other") || this._request.redirectSource) {
2189 this._initiatorCell.removeChildren();
2190 var redirectSource = this._request.redirectSource;
2191 if (redirectSource) {
2192 this._initiatorCell.title = redirectSource.url;
2193 this._initiatorCell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2194 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Redirect"));
2195 } else if (initiator.type === "script") {
2196 var topFrame = initiator.stackTrace[0];
2197 // This could happen when request loading was triggered by console.
2198 if (!topFrame.url) {
2199 this._initiatorCell.addStyleClass("network-dim-cell");
2200 this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
2203 var urlElement = this._parentView._linkifier.linkifyLocation(topFrame.url, topFrame.lineNumber - 1, 0);
2204 urlElement.title = "";
2205 this._initiatorCell.appendChild(urlElement);
2206 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
2207 this._initiatorCell.addStyleClass("network-script-initiated");
2208 this._initiatorCell.request = this._request;
2209 } else { // initiator.type === "parser"
2210 this._initiatorCell.title = initiator.url + ":" + initiator.lineNumber;
2211 this._initiatorCell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2212 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Parser"));
2215 this._initiatorCell.addStyleClass("network-dim-cell");
2216 this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
2220 _refreshCookiesCell: function()
2222 var requestCookies = this._request.requestCookies;
2223 this._cookiesCell.setTextAndTitle(requestCookies ? "" + requestCookies.length : "");
2226 _refreshSetCookiesCell: function()
2228 var responseCookies = this._request.responseCookies;
2229 this._setCookiesCell.setTextAndTitle(responseCookies ? "" + responseCookies.length : "");
2232 _refreshSizeCell: function()
2234 if (this._request.cached) {
2235 this._sizeCell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2236 this._sizeCell.addStyleClass("network-dim-cell");
2238 var resourceSize = typeof this._request.resourceSize === "number" ? Number.bytesToString(this._request.resourceSize) : "?";
2239 var transferSize = typeof this._request.transferSize === "number" ? Number.bytesToString(this._request.transferSize) : "?";
2240 this._sizeCell.setTextAndTitle(transferSize);
2241 this._sizeCell.removeStyleClass("network-dim-cell");
2242 this._appendSubtitle(this._sizeCell, resourceSize);
2246 _refreshTimeCell: function()
2248 if (this._request.duration > 0) {
2249 this._timeCell.removeStyleClass("network-dim-cell");
2250 this._timeCell.setTextAndTitle(Number.secondsToString(this._request.duration));
2251 this._appendSubtitle(this._timeCell, Number.secondsToString(this._request.latency));
2253 this._timeCell.addStyleClass("network-dim-cell");
2254 this._timeCell.setTextAndTitle(WebInspector.UIString("Pending"));
2258 _appendSubtitle: function(cellElement, subtitleText)
2260 var subtitleElement = document.createElement("div");
2261 subtitleElement.className = "network-cell-subtitle";
2262 subtitleElement.textContent = subtitleText;
2263 cellElement.appendChild(subtitleElement);
2266 refreshGraph: function(calculator)
2268 var percentages = calculator.computeBarGraphPercentages(this._request);
2269 this._percentages = percentages;
2271 this._barAreaElement.removeStyleClass("hidden");
2272 this._updateElementStyleClasses(this._graphElement);
2274 this._barLeftElement.style.setProperty("left", percentages.start + "%");
2275 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2277 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
2278 this._barRightElement.style.setProperty("left", percentages.middle + "%");
2280 var labels = calculator.computeBarGraphLabels(this._request);
2281 this._labelLeftElement.textContent = labels.left;
2282 this._labelRightElement.textContent = labels.right;
2284 var tooltip = (labels.tooltip || "");
2285 this._barLeftElement.title = tooltip;
2286 this._labelLeftElement.title = tooltip;
2287 this._labelRightElement.title = tooltip;
2288 this._barRightElement.title = tooltip;
2291 _refreshLabelPositions: function()
2293 if (!this._percentages)
2295 this._labelLeftElement.style.removeProperty("left");
2296 this._labelLeftElement.style.removeProperty("right");
2297 this._labelLeftElement.removeStyleClass("before");
2298 this._labelLeftElement.removeStyleClass("hidden");
2300 this._labelRightElement.style.removeProperty("left");
2301 this._labelRightElement.style.removeProperty("right");
2302 this._labelRightElement.removeStyleClass("after");
2303 this._labelRightElement.removeStyleClass("hidden");
2305 const labelPadding = 10;
2306 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2307 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2309 if (this._barLeftElement) {
2310 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2311 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2313 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2314 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2317 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2318 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2320 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2321 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2322 const graphElementOffsetWidth = this._graphElement.offsetWidth;
2324 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2325 var leftHidden = true;
2327 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2328 var rightHidden = true;
2330 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2331 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2332 if (labelBefore && !labelAfter)
2334 else if (labelAfter && !labelBefore)
2340 this._labelLeftElement.addStyleClass("hidden");
2341 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2342 this._labelLeftElement.addStyleClass("before");
2344 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2345 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2350 this._labelRightElement.addStyleClass("hidden");
2351 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2352 this._labelRightElement.addStyleClass("after");
2354 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2355 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2359 __proto__: WebInspector.DataGridNode.prototype
2363 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2365 var aFileName = a._request.name();
2366 var bFileName = b._request.name();
2367 if (aFileName > bFileName)
2369 if (bFileName > aFileName)
2374 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2376 if (b._request.cached && !a._request.cached)
2378 if (a._request.cached && !b._request.cached)
2381 if (a._request.transferSize === b._request.transferSize)
2384 return a._request.transferSize - b._request.transferSize;
2387 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2389 if (!a._request.initiator || a._request.initiator.type === "Other")
2391 if (!b._request.initiator || b._request.initiator.type === "Other")
2394 if (a._request.initiator.url < b._request.initiator.url)
2396 if (a._request.initiator.url > b._request.initiator.url)
2399 return a._request.initiator.lineNumber - b._request.initiator.lineNumber;
2402 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
2404 var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
2405 var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
2406 return aScore - bScore;
2409 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
2411 var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
2412 var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
2413 return aScore - bScore;
2416 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
2418 var aValue = a._request[propertyName];
2419 var bValue = b._request[propertyName];
2420 if (aValue > bValue)
2421 return revert ? -1 : 1;
2422 if (bValue > aValue)
2423 return revert ? 1 : -1;