2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 2009, 2010 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 WebInspector.NetworkPanel = function()
33 WebInspector.Panel.call(this, "network");
36 this.sidebarElement.className = "network-sidebar";
39 this._resourcesById = {};
40 this._resourcesByURL = {};
41 this._lastIdentifier = 0;
42 this._staleResources = [];
43 this._resourceGridNodes = {};
44 this._mainResourceLoadTime = -1;
45 this._mainResourceDOMContentTime = -1;
46 this._hiddenCategories = {};
48 this._categories = WebInspector.resourceCategories;
50 this.containerElement = document.createElement("div");
51 this.containerElement.id = "network-container";
52 this.sidebarElement.appendChild(this.containerElement);
54 this._viewsContainerElement = document.createElement("div");
55 this._viewsContainerElement.id = "network-views";
56 this._viewsContainerElement.className = "hidden";
57 this.element.appendChild(this._viewsContainerElement);
59 this._closeButtonElement = document.createElement("button");
60 this._closeButtonElement.id = "network-close-button";
61 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
62 this._viewsContainerElement.appendChild(this._closeButtonElement);
64 this._createSortingFunctions();
66 this._createTimelineGrid();
67 this._createStatusbarButtons();
68 this._createFilterStatusBarItems();
69 this._createSummaryBar();
71 if (!WebInspector.settings.resourcesLargeRows)
72 this._setLargerResources(WebInspector.settings.resourcesLargeRows);
74 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), true);
76 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
77 this._filter(this._filterAllElement, false);
79 this._toggleGridMode();
82 WebInspector.NetworkPanel.prototype = {
83 get toolbarItemLabel()
85 return WebInspector.UIString("Network");
90 return [this._largerResourcesButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement];
93 isCategoryVisible: function(categoryName)
98 elementsToRestoreScrollPositionsFor: function()
100 return [this.containerElement];
105 WebInspector.Panel.prototype.resize.call(this);
106 this._dataGrid.updateWidths();
107 this._positionSummaryBar();
110 updateSidebarWidth: function(width)
112 if (!this._viewingResourceMode)
114 WebInspector.Panel.prototype.updateSidebarWidth.call(this, width);
115 if (this._summaryBarElement.parentElement === this.element)
116 this._summaryBarElement.style.width = width + "px";
119 updateMainViewWidth: function(width)
121 this._viewsContainerElement.style.left = width + "px";
124 handleShortcut: function(event)
126 if (this._viewingResourceMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
127 this._toggleGridMode();
128 event.handled = true;
132 _positionSummaryBar: function()
134 // Position the total bar.
136 var fillerRow = this._dataGrid.dataTableBody.lastChild;
137 if (this._summaryBarElement.parentElement !== this.element && fillerRow.offsetHeight > 0) {
138 // Glue status to bottom.
139 if (this._summaryBarRowNode) {
140 this._dataGrid.removeChild(this._summaryBarRowNode);
141 delete this._summaryBarRowNode;
143 this._summaryBarElement.addStyleClass("network-summary-bar-bottom");
144 this._summaryBarElement.style.setProperty("width", this.sidebarElement.offsetWidth + "px");
145 this.element.appendChild(this._summaryBarElement);
146 this._dataGrid.element.style.bottom = "20px";
150 if (!this._summaryBarRowNode && !fillerRow.offsetHeight) {
151 // Glue status to table.
152 this._summaryBarRowNode = new WebInspector.NetworkTotalGridNode(this._summaryBarElement);
153 this._summaryBarElement.removeStyleClass("network-summary-bar-bottom");
154 this._summaryBarElement.style.removeProperty("width");
155 this._dataGrid.appendChild(this._summaryBarRowNode);
156 this._dataGrid.element.style.bottom = 0;
161 _resetSummaryBar: function()
163 delete this._summaryBarRowNode;
164 this._summaryBarElement.parentElement.removeChild(this._summaryBarElement);
165 this._updateSummaryBar();
168 _createTimelineGrid: function()
170 this._timelineGrid = new WebInspector.TimelineGrid();
171 this._timelineGrid.element.addStyleClass("network-timeline-grid");
172 this._dataGrid.element.appendChild(this._timelineGrid.element);
175 _createTable: function()
177 var columns = {name: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}};
178 columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path"));
179 columns.name.sortable = true;
180 columns.name.width = "20%";
181 columns.name.disclosure = true;
183 columns.method.title = WebInspector.UIString("Method");
184 columns.method.sortable = true;
185 columns.method.width = "7%";
187 columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text"));
188 columns.status.sortable = true;
189 columns.status.width = "8%";
191 columns.type.title = WebInspector.UIString("Type");
192 columns.type.sortable = true;
193 columns.type.width = "10%";
195 columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Transfer"));
196 columns.size.sortable = true;
197 columns.size.width = "10%";
198 columns.size.aligned = "right";
200 columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency"));
201 columns.time.sortable = true;
202 columns.time.width = "10%";
203 columns.time.aligned = "right";
205 columns.timeline.title = "";
206 columns.timeline.sortable = false;
207 columns.timeline.width = "37%";
208 columns.timeline.sort = "ascending";
210 this._dataGrid = new WebInspector.DataGrid(columns);
211 this.containerElement.appendChild(this._dataGrid.element);
212 this._dataGrid.addEventListener("sorting changed", this._sortItems, this);
213 this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this);
215 this._patchTimelineHeader();
218 _makeHeaderFragment: function(title, subtitle)
220 var fragment = document.createDocumentFragment();
221 fragment.appendChild(document.createTextNode(title));
222 var subtitleDiv = document.createElement("div");
223 subtitleDiv.className = "network-header-subtitle";
224 subtitleDiv.textContent = subtitle;
225 fragment.appendChild(subtitleDiv);
229 _patchTimelineHeader: function()
231 var timelineSorting = document.createElement("select");
233 var option = document.createElement("option");
234 option.value = "startTime";
235 option.label = WebInspector.UIString("Timeline");
236 timelineSorting.appendChild(option);
238 option = document.createElement("option");
239 option.value = "startTime";
240 option.label = WebInspector.UIString("Start Time");
241 timelineSorting.appendChild(option);
243 option = document.createElement("option");
244 option.value = "responseTime";
245 option.label = WebInspector.UIString("Response Time");
246 timelineSorting.appendChild(option);
248 option = document.createElement("option");
249 option.value = "endTime";
250 option.label = WebInspector.UIString("End Time");
251 timelineSorting.appendChild(option);
253 option = document.createElement("option");
254 option.value = "duration";
255 option.label = WebInspector.UIString("Duration");
256 timelineSorting.appendChild(option);
258 option = document.createElement("option");
259 option.value = "latency";
260 option.label = WebInspector.UIString("Latency");
261 timelineSorting.appendChild(option);
263 var header = this._dataGrid.headerTableHeader("timeline");
264 header.replaceChild(timelineSorting, header.firstChild);
266 timelineSorting.addEventListener("click", function(event) { event.stopPropagation() }, false);
267 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
268 this._timelineSortSelector = timelineSorting;
271 _createSortingFunctions: function()
273 this._sortingFunctions = {};
274 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
275 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "method", false);
276 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode", false);
277 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType", false);
278 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
279 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", false);
280 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
281 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
282 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "endTime", false);
283 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "responseReceivedTime", false);
284 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", true);
285 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "latency", true);
287 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
288 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
290 this._calculators = {};
291 this._calculators.timeline = timeCalculator;
292 this._calculators.startTime = timeCalculator;
293 this._calculators.endTime = timeCalculator;
294 this._calculators.responseTime = timeCalculator;
295 this._calculators.duration = durationCalculator;
296 this._calculators.latency = durationCalculator;
299 _sortItems: function()
301 var columnIdentifier = this._dataGrid.sortColumnIdentifier;
302 if (columnIdentifier === "timeline") {
303 this._sortByTimeline();
306 var sortingFunction = this._sortingFunctions[columnIdentifier];
307 if (!sortingFunction)
310 this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending");
311 this._timelineSortSelector.selectedIndex = 0;
314 _sortByTimeline: function()
316 var selectedIndex = this._timelineSortSelector.selectedIndex;
318 selectedIndex = 1; // Sort by start time by default.
319 var selectedOption = this._timelineSortSelector[selectedIndex];
320 var value = selectedOption.value;
322 var sortingFunction = this._sortingFunctions[value];
323 this._dataGrid.sortNodes(sortingFunction);
324 this.calculator = this._calculators[value];
325 if (this.calculator.startAtZero)
326 this._timelineGrid.hideEventDividers();
328 this._timelineGrid.showEventDividers();
329 this._dataGrid.markColumnAsSortedBy("timeline", "ascending");
332 _createFilterStatusBarItems: function()
334 var filterBarElement = document.createElement("div");
335 filterBarElement.className = "scope-bar status-bar-item";
336 filterBarElement.id = "network-filter";
338 function createFilterElement(category, label)
340 var categoryElement = document.createElement("li");
341 categoryElement.category = category;
342 categoryElement.className = category;
343 categoryElement.appendChild(document.createTextNode(label));
344 categoryElement.addEventListener("click", this._updateFilter.bind(this), false);
345 filterBarElement.appendChild(categoryElement);
347 return categoryElement;
350 this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
353 var dividerElement = document.createElement("div");
354 dividerElement.addStyleClass("scope-bar-divider");
355 filterBarElement.appendChild(dividerElement);
357 for (var category in this._categories)
358 createFilterElement.call(this, category, this._categories[category].title);
359 this._filterBarElement = filterBarElement;
362 _createSummaryBar: function()
364 this._summaryBarElement = document.createElement("div");
365 this._summaryBarElement.className = "network-summary-bar";
366 this.containerElement.appendChild(this._summaryBarElement);
369 _updateSummaryBar: function()
371 this._positionSummaryBar(); // Grid is growing.
372 var numRequests = this._resources.length;
375 if (this._summaryBarElement._isDisplayingWarning)
377 this._summaryBarElement._isDisplayingWarning = true;
379 var img = document.createElement("img");
380 img.src = "Images/warningIcon.png";
381 this._summaryBarElement.removeChildren();
382 this._summaryBarElement.appendChild(img);
383 this._summaryBarElement.appendChild(document.createTextNode(" "));
384 this._summaryBarElement.appendChild(document.createTextNode(
385 WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
388 delete this._summaryBarElement._isDisplayingWarning;
390 var transferSize = 0;
393 for (var i = 0; i < this._resources.length; ++i) {
394 var resource = this._resources[i];
395 transferSize += resource.cached ? 0 : resource.transferSize;
396 if (resource.isMainResource)
397 baseTime = resource.startTime;
398 if (resource.endTime > maxTime)
399 maxTime = resource.endTime;
401 var text = String.sprintf(WebInspector.UIString("%d requests"), numRequests);
402 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
403 if (baseTime !== -1 && this._mainResourceLoadTime !== -1 && this._mainResourceDOMContentTime !== -1 && this._mainResourceDOMContentTime > baseTime) {
404 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
405 Number.secondsToString(maxTime - baseTime),
406 Number.secondsToString(this._mainResourceLoadTime - baseTime),
407 Number.secondsToString(this._mainResourceDOMContentTime - baseTime));
409 this._summaryBarElement.textContent = text;
412 _showCategory: function(category)
414 this._dataGrid.element.addStyleClass("filter-" + category);
415 delete this._hiddenCategories[category];
418 _hideCategory: function(category)
420 this._dataGrid.element.removeStyleClass("filter-" + category);
421 this._hiddenCategories[category] = true;
424 _updateFilter: function(e)
426 var isMac = WebInspector.isMac();
427 var selectMultiple = false;
428 if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
429 selectMultiple = true;
430 if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
431 selectMultiple = true;
433 this._filter(e.target, selectMultiple);
434 this._positionSummaryBar();
437 _filter: function(target, selectMultiple)
439 function unselectAll()
441 for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) {
442 var child = this._filterBarElement.childNodes[i];
446 child.removeStyleClass("selected");
447 this._hideCategory(child.category);
451 if (target.category === this._filterAllElement) {
452 if (target.hasStyleClass("selected")) {
453 // We can't unselect All, so we break early here
457 // If All wasn't selected, and now is, unselect everything else.
458 unselectAll.call(this);
460 // Something other than All is being selected, so we want to unselect All.
461 if (this._filterAllElement.hasStyleClass("selected")) {
462 this._filterAllElement.removeStyleClass("selected");
463 this._hideCategory("all");
467 if (!selectMultiple) {
468 // If multiple selection is off, we want to unselect everything else
469 // and just select ourselves.
470 unselectAll.call(this);
472 target.addStyleClass("selected");
473 this._showCategory(target.category);
477 if (target.hasStyleClass("selected")) {
478 // If selectMultiple is turned on, and we were selected, we just
479 // want to unselect ourselves.
480 target.removeStyleClass("selected");
481 this._hideCategory(target.category);
483 // If selectMultiple is turned on, and we weren't selected, we just
484 // want to select ourselves.
485 target.addStyleClass("selected");
486 this._showCategory(target.category);
490 _scheduleRefresh: function()
492 if (this._needsRefresh)
495 this._needsRefresh = true;
497 if (this.visible && !("_refreshTimeout" in this))
498 this._refreshTimeout = setTimeout(this.refresh.bind(this), 500);
501 _updateDividersIfNeeded: function(force)
503 var timelineColumn = this._dataGrid.columns.timeline;
504 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
505 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnID) {
506 // Position timline grid location.
507 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
508 this._timelineGrid.element.style.right = "18px";
514 this._scheduleRefresh();
517 proceed = this._timelineGrid.updateDividers(force, this.calculator);
522 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
523 // If our current sorting method starts at zero, that means it shows all
524 // resources starting at the same point, and so onLoad event and DOMContent
525 // event lines really wouldn't make much sense here, so don't render them.
526 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
527 // function defined, we are probably sorting by size, and event times aren't relevant
532 this._timelineGrid.removeEventDividers();
533 if (this._mainResourceLoadTime !== -1) {
534 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceLoadTime);
536 var loadDivider = document.createElement("div");
537 loadDivider.className = "network-event-divider network-red-divider";
539 var loadDividerPadding = document.createElement("div");
540 loadDividerPadding.className = "network-event-divider-padding";
541 loadDividerPadding.title = WebInspector.UIString("Load event fired");
542 loadDividerPadding.appendChild(loadDivider);
543 loadDividerPadding.style.left = percent + "%";
544 this._timelineGrid.addEventDivider(loadDividerPadding);
547 if (this._mainResourceDOMContentTime !== -1) {
548 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceDOMContentTime);
550 var domContentDivider = document.createElement("div");
551 domContentDivider.className = "network-event-divider network-blue-divider";
553 var domContentDividerPadding = document.createElement("div");
554 domContentDividerPadding.className = "network-event-divider-padding";
555 domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
556 domContentDividerPadding.appendChild(domContentDivider);
557 domContentDividerPadding.style.left = percent + "%";
558 this._timelineGrid.addEventDivider(domContentDividerPadding);
562 _refreshIfNeeded: function()
564 if (this._needsRefresh)
568 _invalidateAllItems: function()
570 this._staleResources = this._resources.slice();
575 return this._calculator;
580 if (!x || this._calculator === x)
583 this._calculator = x;
584 this._calculator.reset();
586 this._invalidateAllItems();
590 _resourceGridNode: function(resource)
592 return this._resourceGridNodes[resource.identifier];
595 revealAndSelectItem: function(resource)
597 var node = this._resourceGridNode(resource);
604 addEventDivider: function(divider)
606 this._timelineGrid.addEventDivider(divider);
609 _createStatusbarButtons: function()
611 this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
612 this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked.bind(this), false);
614 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
615 this._clearButton.addEventListener("click", this._reset.bind(this), false);
617 this._largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
618 this._largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows;
619 this._largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
622 set mainResourceLoadTime(x)
624 if (this._mainResourceLoadTime === x)
627 this._mainResourceLoadTime = x || -1;
628 // Update the dividers to draw the new line
629 this._updateDividersIfNeeded(true);
632 set mainResourceDOMContentTime(x)
634 if (this._mainResourceDOMContentTime === x)
637 this._mainResourceDOMContentTime = x || -1;
638 this._updateDividersIfNeeded(true);
643 WebInspector.Panel.prototype.show.call(this);
645 this._refreshIfNeeded();
647 var visibleView = this.visibleView;
648 if (this.visibleResource) {
649 this.visibleView.headersVisible = true;
650 this.visibleView.show(this._viewsContainerElement);
651 } else if (visibleView)
654 // Hide any views that are visible that are not this panel's current visible view.
655 // This can happen when a ResourceView is visible in the Scripts panel then switched
656 // to the this panel.
657 var resourcesLength = this._resources.length;
658 for (var i = 0; i < resourcesLength; ++i) {
659 var resource = this._resources[i];
660 var view = resource._resourcesView;
661 if (!view || view === visibleView)
663 view.visible = false;
665 this._dataGrid.updateWidths();
666 this._positionSummaryBar();
671 WebInspector.Panel.prototype.hide.call(this);
672 this._popoverHelper.hidePopup();
675 get searchableViews()
681 searchMatchFound: function(view, matches)
683 this._resourceGridNode(view.resource).searchMatches = matches;
686 searchCanceled: function(startingNewSearch)
688 WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
690 if (startingNewSearch || !this._resources)
694 performSearch: function(query)
696 WebInspector.Panel.prototype.performSearch.call(this, query);
701 if (this.visibleResource)
702 return this.visibleResource._resourcesView;
708 this._needsRefresh = false;
709 if ("_refreshTimeout" in this) {
710 clearTimeout(this._refreshTimeout);
711 delete this._refreshTimeout;
714 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
715 var staleItemsLength = this._staleResources.length;
716 var boundariesChanged = false;
718 for (var i = 0; i < staleItemsLength; ++i) {
719 var resource = this._staleResources[i];
720 var node = this._resourceGridNode(resource);
722 // Create the timeline tree element and graph.
723 node = new WebInspector.NetworkDataGridNode(this, resource);
724 this._resourceGridNodes[resource.identifier] = node;
725 this._dataGrid.appendChild(node);
727 node.refreshResource();
729 if (this.calculator.updateBoundaries(resource))
730 boundariesChanged = true;
733 if (boundariesChanged) {
734 // The boundaries changed, so all item graphs are stale.
735 this._invalidateAllItems();
736 staleItemsLength = this._staleResources.length;
739 for (var i = 0; i < staleItemsLength; ++i)
740 this._resourceGridNode(this._staleResources[i]).refreshGraph(this.calculator);
742 this._staleResources = [];
744 this._updateSummaryBar();
745 this._dataGrid.updateWidths();
747 if (wasScrolledToLastRow)
748 this._dataGrid.scrollToLastRow();
751 _onPreserveLogClicked: function(e)
753 this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
758 if (!this._preserveLogToggle.toggled)
764 this._popoverHelper.hidePopup();
765 this._closeVisibleResource();
767 this._toggleGridMode();
769 // Begin reset timeline
770 if (this._calculator)
771 this._calculator.reset();
773 this._resources = [];
774 this._resourcesById = {};
775 this._resourcesByURL = {};
776 this._staleResources = [];
777 this._resourceGridNodes = {};
779 this._dataGrid.removeChildren();
780 delete this._summaryBarRowNode;
781 this._updateDividersIfNeeded(true);
782 // End reset timeline.
784 this._mainResourceLoadTime = -1;
785 this._mainResourceDOMContentTime = -1;
787 this._viewsContainerElement.removeChildren();
788 this._viewsContainerElement.appendChild(this._closeButtonElement);
789 this._resetSummaryBar();
794 return this._resourcesById;
797 refreshResource: function(resource)
799 if (!resource.identifier)
800 resource.identifier = "network:" + this._lastIdentifier++;
802 if (!this._resourcesById[resource.identifier]) {
803 this._resources.push(resource);
804 this._resourcesById[resource.identifier] = resource;
805 this._resourcesByURL[resource.url] = resource;
807 // Pull all the redirects of the main resource upon commit load.
808 if (resource.redirects) {
809 for (var i = 0; i < resource.redirects.length; ++i)
810 this.refreshResource(resource.redirects[i]);
814 this._staleResources.push(resource);
815 this._scheduleRefresh();
817 if (!resource || !resource._resourcesView)
820 if (WebInspector.ResourceManager.resourceViewTypeMatchesResource(resource, resource._resourcesView))
822 var newView = WebInspector.ResourceManager.createResourceView(resource);
824 var oldView = resource._resourcesView;
825 var oldViewParentNode = oldView.visible ? oldView.element.parentNode : null;
827 resource._resourcesView.detach();
828 delete resource._resourcesView;
830 resource._resourcesView = newView;
832 newView.headersVisible = oldView.headersVisible;
834 if (oldViewParentNode)
835 newView.show(oldViewParentNode);
837 WebInspector.panels.scripts.viewRecreated(oldView, newView);
840 canShowSourceLine: function(url, line)
842 return !!this._resourcesByURL[url];
845 showSourceLine: function(url, line)
847 this._showResource(this._resourcesByURL[url], line);
850 _showResource: function(resource, line)
855 this._popoverHelper.hidePopup();
857 this._toggleViewingResourceMode();
859 if (this.visibleResource && this.visibleResource._resourcesView)
860 this.visibleResource._resourcesView.hide();
862 var view = WebInspector.ResourceManager.resourceViewForResource(resource);
863 view.headersVisible = true;
864 view.show(this._viewsContainerElement);
867 view.selectContentTab(true);
869 view.revealLine(line);
870 if (view.highlightLine)
871 view.highlightLine(line);
874 this.visibleResource = resource;
875 this.updateSidebarWidth();
878 _closeVisibleResource: function()
880 this.element.removeStyleClass("viewing-resource");
882 if (this.visibleResource && this.visibleResource._resourcesView)
883 this.visibleResource._resourcesView.hide();
884 delete this.visibleResource;
886 if (this._lastSelectedGraphTreeElement)
887 this._lastSelectedGraphTreeElement.select(true);
889 this.updateSidebarWidth();
892 _toggleLargerResources: function()
894 WebInspector.settings.resourcesLargeRows = !WebInspector.settings.resourcesLargeRows;
895 this._setLargerResources(WebInspector.settings.resourcesLargeRows);
898 _setLargerResources: function(enabled)
900 this._largerResourcesButton.toggled = enabled;
902 this._largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
903 this._dataGrid.element.addStyleClass("small");
904 this._timelineGrid.element.addStyleClass("small");
905 this._viewsContainerElement.addStyleClass("small");
907 this._largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
908 this._dataGrid.element.removeStyleClass("small");
909 this._timelineGrid.element.removeStyleClass("small");
910 this._viewsContainerElement.removeStyleClass("small");
912 this._positionSummaryBar();
915 _getPopoverAnchor: function(element)
917 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
920 var resource = anchor.parentElement.resource;
921 return resource && resource.timing ? anchor : null;
924 _showPopover: function(anchor)
926 var tableElement = document.createElement("table");
927 var resource = anchor.parentElement.resource;
930 function addRow(title, start, end, color)
939 if (resource.timing.proxyStart !== -1)
940 addRow(WebInspector.UIString("Proxy"), resource.timing.proxyStart, resource.timing.proxyEnd);
942 if (resource.timing.dnsStart !== -1)
943 addRow(WebInspector.UIString("DNS Lookup"), resource.timing.dnsStart, resource.timing.dnsEnd);
945 if (resource.timing.connectStart !== -1) {
946 if (resource.connectionReused)
947 addRow(WebInspector.UIString("Blocking"), resource.timing.connectStart, resource.timing.connectEnd);
949 var connectStart = resource.timing.connectStart;
950 // Connection includes DNS, subtract it here.
951 if (resource.timing.dnsStart !== -1)
952 connectStart += resource.timing.dnsEnd - resource.timing.dnsStart;
953 addRow(WebInspector.UIString("Connecting"), connectStart, resource.timing.connectEnd);
957 if (resource.timing.sslStart !== -1)
958 addRow(WebInspector.UIString("SSL"), resource.timing.sslStart, resource.timing.sslEnd);
960 var sendStart = resource.timing.sendStart;
961 if (resource.timing.sslStart !== -1)
962 sendStart += resource.timing.sslEnd - resource.timing.sslStart;
964 addRow(WebInspector.UIString("Sending"), resource.timing.sendStart, resource.timing.sendEnd);
965 addRow(WebInspector.UIString("Waiting"), resource.timing.sendEnd, resource.timing.receiveHeadersEnd);
966 addRow(WebInspector.UIString("Receiving"), (resource.responseReceivedTime - resource.timing.requestTime) * 1000, (resource.endTime - resource.timing.requestTime) * 1000);
968 const chartWidth = 200;
969 var total = (resource.endTime - resource.timing.requestTime) * 1000;
970 var scale = chartWidth / total;
972 for (var i = 0; i < rows.length; ++i) {
973 var tr = document.createElement("tr");
974 tableElement.appendChild(tr);
976 var td = document.createElement("td");
977 td.textContent = rows[i].title;
980 td = document.createElement("td");
981 td.width = chartWidth + "px";
983 var row = document.createElement("div");
984 row.className = "network-timing-row";
987 var bar = document.createElement("span");
988 bar.className = "network-timing-bar";
989 bar.style.left = scale * rows[i].start + "px";
990 bar.style.right = scale * (total - rows[i].end) + "px";
991 bar.style.backgroundColor = rows[i].color;
992 bar.textContent = "\u200B"; // Important for 0-time items to have 0 width.
993 row.appendChild(bar);
995 var title = document.createElement("span");
996 title.className = "network-timing-bar-title";
997 if (total - rows[i].end < rows[i].start)
998 title.style.right = (scale * (total - rows[i].end) + 3) + "px";
1000 title.style.left = (scale * rows[i].start + 3) + "px";
1001 title.textContent = Number.millisToString(rows[i].end - rows[i].start);
1002 row.appendChild(title);
1007 var popover = new WebInspector.Popover(tableElement);
1008 popover.show(anchor);
1012 _toggleGridMode: function()
1014 if (this._viewingResourceMode) {
1015 this._viewingResourceMode = false;
1016 this.element.removeStyleClass("viewing-resource");
1017 this._dataGrid.element.removeStyleClass("viewing-resource-mode");
1018 this._viewsContainerElement.addStyleClass("hidden");
1019 this.sidebarElement.style.right = 0;
1020 this.sidebarElement.style.removeProperty("width");
1021 this._summaryBarElement.style.removeProperty("width");
1024 if (this._briefGrid) {
1025 this._dataGrid.element.removeStyleClass("full-grid-mode");
1026 this._dataGrid.element.addStyleClass("brief-grid-mode");
1028 this._dataGrid.hideColumn("method");
1029 this._dataGrid.hideColumn("status");
1030 this._dataGrid.hideColumn("type");
1031 this._dataGrid.hideColumn("size");
1032 this._dataGrid.hideColumn("time");
1036 widths.timeline = 80;
1038 this._dataGrid.element.addStyleClass("full-grid-mode");
1039 this._dataGrid.element.removeStyleClass("brief-grid-mode");
1041 this._dataGrid.showColumn("method");
1042 this._dataGrid.showColumn("status");
1043 this._dataGrid.showColumn("type");
1044 this._dataGrid.showColumn("size");
1045 this._dataGrid.showColumn("time");
1054 widths.timeline = 37;
1057 this._dataGrid.showColumn("timeline");
1058 this._dataGrid.applyColumnWidthsMap(widths);
1062 _toggleViewingResourceMode: function()
1064 if (this._viewingResourceMode)
1066 this._viewingResourceMode = true;
1067 this._preservedColumnWidths = this._dataGrid.columnWidthsMap();
1069 this.element.addStyleClass("viewing-resource");
1070 this._dataGrid.element.addStyleClass("viewing-resource-mode");
1071 this._dataGrid.element.removeStyleClass("full-grid-mode");
1072 this._dataGrid.element.removeStyleClass("brief-grid-mode");
1074 this._dataGrid.hideColumn("method");
1075 this._dataGrid.hideColumn("status");
1076 this._dataGrid.hideColumn("type");
1077 this._dataGrid.hideColumn("size");
1078 this._dataGrid.hideColumn("time");
1079 this._dataGrid.hideColumn("timeline");
1081 this._viewsContainerElement.removeStyleClass("hidden");
1082 this.updateSidebarWidth(200);
1086 this._dataGrid.applyColumnWidthsMap(widths);
1090 WebInspector.NetworkPanel.prototype.__proto__ = WebInspector.Panel.prototype;
1092 WebInspector.NetworkBaseCalculator = function()
1096 WebInspector.NetworkBaseCalculator.prototype = {
1097 computeSummaryValues: function(items)
1100 var categoryValues = {};
1102 var itemsLength = items.length;
1103 for (var i = 0; i < itemsLength; ++i) {
1104 var item = items[i];
1105 var value = this._value(item);
1106 if (typeof value === "undefined")
1108 if (!(item.category.name in categoryValues))
1109 categoryValues[item.category.name] = 0;
1110 categoryValues[item.category.name] += value;
1114 return {categoryValues: categoryValues, total: total};
1117 computeBarGraphPercentages: function(item)
1119 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan) * 100};
1122 computeBarGraphLabels: function(item)
1124 const label = this.formatValue(this._value(item));
1125 return {left: label, right: label, tooltip: label};
1130 return this.maximumBoundary - this.minimumBoundary;
1133 updateBoundaries: function(item)
1135 this.minimumBoundary = 0;
1137 var value = this._value(item);
1138 if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) {
1139 this.maximumBoundary = value;
1147 delete this.minimumBoundary;
1148 delete this.maximumBoundary;
1151 _value: function(item)
1156 formatValue: function(value)
1158 return value.toString();
1162 WebInspector.NetworkTimeCalculator = function(startAtZero)
1164 WebInspector.NetworkBaseCalculator.call(this);
1165 this.startAtZero = startAtZero;
1168 WebInspector.NetworkTimeCalculator.prototype = {
1169 computeSummaryValues: function(resources)
1171 var resourcesByCategory = {};
1172 var resourcesLength = resources.length;
1173 for (var i = 0; i < resourcesLength; ++i) {
1174 var resource = resources[i];
1175 if (!(resource.category.name in resourcesByCategory))
1176 resourcesByCategory[resource.category.name] = [];
1177 resourcesByCategory[resource.category.name].push(resource);
1182 var categoryValues = {};
1183 for (var category in resourcesByCategory) {
1184 resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
1185 categoryValues[category] = 0;
1187 var segment = {start: -1, end: -1};
1189 var categoryResources = resourcesByCategory[category];
1190 var resourcesLength = categoryResources.length;
1191 for (var i = 0; i < resourcesLength; ++i) {
1192 var resource = categoryResources[i];
1193 if (resource.startTime === -1 || resource.endTime === -1)
1196 if (typeof earliestStart === "undefined")
1197 earliestStart = resource.startTime;
1199 earliestStart = Math.min(earliestStart, resource.startTime);
1201 if (typeof latestEnd === "undefined")
1202 latestEnd = resource.endTime;
1204 latestEnd = Math.max(latestEnd, resource.endTime);
1206 if (resource.startTime <= segment.end) {
1207 segment.end = Math.max(segment.end, resource.endTime);
1211 categoryValues[category] += segment.end - segment.start;
1213 segment.start = resource.startTime;
1214 segment.end = resource.endTime;
1217 // Add the last segment
1218 categoryValues[category] += segment.end - segment.start;
1221 return {categoryValues: categoryValues, total: latestEnd - earliestStart};
1224 computeBarGraphPercentages: function(resource)
1226 if (resource.startTime !== -1)
1227 var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
1231 if (resource.responseReceivedTime !== -1)
1232 var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
1234 var middle = (this.startAtZero ? start : 100);
1236 if (resource.endTime !== -1)
1237 var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
1239 var end = (this.startAtZero ? middle : 100);
1241 if (this.startAtZero) {
1247 return {start: start, middle: middle, end: end};
1250 computePercentageFromEventTime: function(eventTime)
1252 // This function computes a percentage in terms of the total loading time
1253 // of a specific event. If startAtZero is set, then this is useless, and we
1254 // want to return 0.
1255 if (eventTime !== -1 && !this.startAtZero)
1256 return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100;
1261 computeBarGraphLabels: function(resource)
1263 var rightLabel = "";
1264 if (resource.responseReceivedTime !== -1 && resource.endTime !== -1)
1265 rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime);
1267 var hasLatency = resource.latency > 0;
1269 var leftLabel = this.formatValue(resource.latency);
1271 var leftLabel = rightLabel;
1273 if (resource.timing)
1274 return {left: leftLabel, right: rightLabel};
1276 if (hasLatency && rightLabel) {
1277 var total = this.formatValue(resource.duration);
1278 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
1279 } else if (hasLatency)
1280 var tooltip = WebInspector.UIString("%s latency", leftLabel);
1281 else if (rightLabel)
1282 var tooltip = WebInspector.UIString("%s download", rightLabel);
1284 if (resource.cached)
1285 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
1286 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
1289 updateBoundaries: function(resource)
1291 var didChange = false;
1294 if (this.startAtZero)
1297 lowerBound = this._lowerBound(resource);
1299 if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
1300 this.minimumBoundary = lowerBound;
1304 var upperBound = this._upperBound(resource);
1305 if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
1306 this.maximumBoundary = upperBound;
1313 formatValue: function(value)
1315 return Number.secondsToString(value, WebInspector.UIString);
1318 _lowerBound: function(resource)
1323 _upperBound: function(resource)
1329 WebInspector.NetworkTimeCalculator.prototype.__proto__ = WebInspector.NetworkBaseCalculator.prototype;
1331 WebInspector.NetworkTransferTimeCalculator = function()
1333 WebInspector.NetworkTimeCalculator.call(this, false);
1336 WebInspector.NetworkTransferTimeCalculator.prototype = {
1337 formatValue: function(value)
1339 return Number.secondsToString(value, WebInspector.UIString);
1342 _lowerBound: function(resource)
1344 return resource.startTime;
1347 _upperBound: function(resource)
1349 return resource.endTime;
1353 WebInspector.NetworkTransferTimeCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1355 WebInspector.NetworkTransferDurationCalculator = function()
1357 WebInspector.NetworkTimeCalculator.call(this, true);
1360 WebInspector.NetworkTransferDurationCalculator.prototype = {
1361 formatValue: function(value)
1363 return Number.secondsToString(value, WebInspector.UIString);
1366 _upperBound: function(resource)
1368 return resource.duration;
1372 WebInspector.NetworkTransferDurationCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1374 WebInspector.NetworkDataGridNode = function(panel, resource)
1376 WebInspector.DataGridNode.call(this, {});
1377 this._panel = panel;
1378 this._resource = resource;
1381 WebInspector.NetworkDataGridNode.prototype = {
1382 createCells: function()
1384 this._nameCell = this._createDivInTD("name");
1385 this._methodCell = this._createDivInTD("method");
1386 this._statusCell = this._createDivInTD("status");
1387 this._typeCell = this._createDivInTD("type");
1388 this._sizeCell = this._createDivInTD("size");
1389 this._timeCell = this._createDivInTD("time");
1390 this._createTimelineCell();
1395 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
1396 this._panel._showResource(this._resource);
1401 if (!this._panel._hiddenCategories.all)
1403 if (this._panel._hiddenCategories[this._resource.category.name])
1408 _createDivInTD: function(columnIdentifier)
1410 var td = document.createElement("td");
1411 td.className = columnIdentifier + "-column";
1412 var div = document.createElement("div");
1413 td.appendChild(div);
1414 this._element.appendChild(td);
1418 _createTimelineCell: function()
1420 this._graphElement = document.createElement("div");
1421 this._graphElement.className = "network-graph-side";
1422 this._graphElement.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
1424 this._barAreaElement = document.createElement("div");
1425 // this._barAreaElement.className = "network-graph-bar-area hidden";
1426 this._barAreaElement.className = "network-graph-bar-area";
1427 this._barAreaElement.resource = this._resource;
1428 this._graphElement.appendChild(this._barAreaElement);
1430 this._barLeftElement = document.createElement("div");
1431 this._barLeftElement.className = "network-graph-bar waiting";
1432 this._barAreaElement.appendChild(this._barLeftElement);
1434 this._barRightElement = document.createElement("div");
1435 this._barRightElement.className = "network-graph-bar";
1436 this._barAreaElement.appendChild(this._barRightElement);
1438 this._labelLeftElement = document.createElement("div");
1439 this._labelLeftElement.className = "network-graph-label waiting";
1440 this._barAreaElement.appendChild(this._labelLeftElement);
1442 this._labelRightElement = document.createElement("div");
1443 this._labelRightElement.className = "network-graph-label";
1444 this._barAreaElement.appendChild(this._labelRightElement);
1446 this._timelineCell = document.createElement("td");
1447 this._timelineCell.className = "timeline-column";
1448 this._element.appendChild(this._timelineCell);
1449 this._timelineCell.appendChild(this._graphElement);
1452 refreshResource: function()
1454 this._refreshNameCell();
1456 this._methodCell.textContent = this._resource.requestMethod;
1458 this._refreshStatusCell();
1460 if (this._resource.mimeType) {
1461 this._typeCell.removeStyleClass("network-dim-cell");
1462 this._typeCell.textContent = this._resource.mimeType;
1464 this._typeCell.addStyleClass("network-dim-cell");
1465 this._typeCell.textContent = WebInspector.UIString("Pending");
1468 this._refreshSizeCell();
1469 this._refreshTimeCell();
1471 if (this._resource.cached)
1472 this._graphElement.addStyleClass("resource-cached");
1474 if (!this._element.hasStyleClass("network-category-" + this._resource.category.name)) {
1475 this._element.removeMatchingStyleClasses("network-category-\\w+");
1476 this._element.addStyleClass("network-category-" + this._resource.category.name);
1480 _refreshNameCell: function()
1482 this._nameCell.removeChildren();
1484 if (this._resource.category === WebInspector.resourceCategories.images) {
1485 var previewImage = document.createElement("img");
1486 previewImage.className = "image-network-icon-preview";
1488 function onResourceContent()
1490 previewImage.src = this._resource.contentURL;
1492 if (Preferences.useDataURLForResourceImageIcons)
1493 this._resource.requestContent(onResourceContent.bind(this));
1495 previewImage.src = this._resource.url;
1497 var iconElement = document.createElement("div");
1498 iconElement.className = "icon";
1499 iconElement.appendChild(previewImage);
1501 var iconElement = document.createElement("img");
1502 iconElement.className = "icon";
1504 this._nameCell.appendChild(iconElement);
1505 this._nameCell.appendChild(document.createTextNode(this._fileName()));
1508 var subtitle = this._resource.displayDomain;
1510 if (this._resource.path && this._resource.lastPathComponent) {
1511 var lastPathComponentIndex = this._resource.path.lastIndexOf("/" + this._resource.lastPathComponent);
1512 if (lastPathComponentIndex != -1)
1513 subtitle += this._resource.path.substring(0, lastPathComponentIndex);
1516 this._appendSubtitle(this._nameCell, subtitle);
1517 this._nameCell.title = this._resource.url;
1520 _fileName: function()
1522 var fileName = this._resource.displayName;
1523 if (this._resource.queryString)
1524 fileName += "?" + this._resource.queryString;
1528 _refreshStatusCell: function()
1530 this._statusCell.removeChildren();
1532 if (this._resource.statusCode) {
1533 this._statusCell.appendChild(document.createTextNode(this._resource.statusCode));
1534 this._statusCell.removeStyleClass("network-dim-cell");
1535 this._appendSubtitle(this._statusCell, this._resource.statusText);
1536 this._statusCell.title = this._resource.statusCode + " " + this._resource.statusText;
1538 this._statusCell.addStyleClass("network-dim-cell");
1539 this._statusCell.textContent = WebInspector.UIString("Pending");
1543 _refreshSizeCell: function()
1545 var resourceSize = typeof this._resource.resourceSize === "number" ? Number.bytesToString(this._resource.resourceSize) : "?";
1546 var transferSize = typeof this._resource.transferSize === "number" ? Number.bytesToString(this._resource.transferSize) : "?";
1547 var fromCache = this._resource.cached;
1548 this._sizeCell.textContent = !fromCache ? resourceSize : WebInspector.UIString("(from cache)");
1550 this._sizeCell.addStyleClass("network-dim-cell");
1552 this._sizeCell.removeStyleClass("network-dim-cell");
1554 this._appendSubtitle(this._sizeCell, transferSize);
1557 _refreshTimeCell: function()
1559 if (this._resource.duration > 0) {
1560 this._timeCell.removeStyleClass("network-dim-cell");
1561 this._timeCell.textContent = Number.secondsToString(this._resource.duration);
1562 this._appendSubtitle(this._timeCell, Number.secondsToString(this._resource.latency));
1564 this._timeCell.addStyleClass("network-dim-cell");
1565 this._timeCell.textContent = WebInspector.UIString("Pending");
1569 _appendSubtitle: function(cellElement, subtitleText)
1571 var subtitleElement = document.createElement("div");
1572 subtitleElement.className = "network-cell-subtitle";
1573 subtitleElement.textContent = subtitleText;
1574 cellElement.appendChild(subtitleElement);
1577 refreshGraph: function(calculator)
1579 var percentages = calculator.computeBarGraphPercentages(this._resource);
1580 var labels = calculator.computeBarGraphLabels(this._resource);
1582 this._percentages = percentages;
1584 this._barAreaElement.removeStyleClass("hidden");
1586 if (!this._graphElement.hasStyleClass("network-category-" + this._resource.category.name)) {
1587 this._graphElement.removeMatchingStyleClasses("network-category-\\w+");
1588 this._graphElement.addStyleClass("network-category-" + this._resource.category.name);
1591 this._barLeftElement.style.setProperty("left", percentages.start + "%");
1592 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
1594 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
1595 this._barRightElement.style.setProperty("left", percentages.middle + "%");
1597 this._labelLeftElement.textContent = labels.left;
1598 this._labelRightElement.textContent = labels.right;
1600 var tooltip = (labels.tooltip || "");
1601 this._barLeftElement.title = tooltip;
1602 this._labelLeftElement.title = tooltip;
1603 this._labelRightElement.title = tooltip;
1604 this._barRightElement.title = tooltip;
1607 _refreshLabelPositions: function()
1609 if (!this._percentages)
1611 this._labelLeftElement.style.removeProperty("left");
1612 this._labelLeftElement.style.removeProperty("right");
1613 this._labelLeftElement.removeStyleClass("before");
1614 this._labelLeftElement.removeStyleClass("hidden");
1616 this._labelRightElement.style.removeProperty("left");
1617 this._labelRightElement.style.removeProperty("right");
1618 this._labelRightElement.removeStyleClass("after");
1619 this._labelRightElement.removeStyleClass("hidden");
1621 const labelPadding = 10;
1622 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
1623 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
1625 if (this._barLeftElement) {
1626 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
1627 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
1629 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
1630 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
1633 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
1634 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
1636 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
1637 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
1638 const graphElementOffsetWidth = this._graphElement.offsetWidth;
1640 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
1641 var leftHidden = true;
1643 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
1644 var rightHidden = true;
1646 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
1647 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
1648 if (labelBefore && !labelAfter)
1650 else if (labelAfter && !labelBefore)
1656 this._labelLeftElement.addStyleClass("hidden");
1657 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
1658 this._labelLeftElement.addStyleClass("before");
1660 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
1661 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
1666 this._labelRightElement.addStyleClass("hidden");
1667 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
1668 this._labelRightElement.addStyleClass("after");
1670 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
1671 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
1676 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
1678 var aFileName = a._resource.displayName + (a._resource.queryString ? a._resource.queryString : "");
1679 var bFileName = b._resource.displayName + (b._resource.queryString ? b._resource.queryString : "");
1680 if (aFileName > bFileName)
1682 if (bFileName > aFileName)
1687 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
1689 if (b._resource.cached && !a._resource.cached)
1691 if (a._resource.cached && !b._resource.cached)
1694 if (a._resource.resourceSize === b._resource.resourceSize)
1697 return a._resource.resourceSize - b._resource.resourceSize;
1700 WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyName, revert, a, b)
1702 var aValue = a._resource[propertyName];
1703 var bValue = b._resource[propertyName];
1704 if (aValue > bValue)
1705 return revert ? -1 : 1;
1706 if (bValue > aValue)
1707 return revert ? 1 : -1;
1711 WebInspector.NetworkDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
1713 WebInspector.NetworkTotalGridNode = function(element)
1715 this._summaryBarElement = element;
1716 WebInspector.DataGridNode.call(this, {summaryRow: true});
1719 WebInspector.NetworkTotalGridNode.prototype = {
1720 createCells: function()
1722 var td = document.createElement("td");
1723 td.setAttribute("colspan", 7);
1724 td.className = "network-summary";
1725 td.appendChild(this._summaryBarElement);
1726 this._element.appendChild(td);
1730 WebInspector.NetworkTotalGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;