2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 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);
75 // Enable faster hint.
76 this._popoverHelper.setTimeout(100);
78 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
79 this._filter(this._filterAllElement, false);
81 this._toggleGridMode();
84 WebInspector.NetworkPanel.prototype = {
85 get toolbarItemLabel()
87 return WebInspector.UIString("Network");
92 return [this._largerResourcesButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement];
95 isCategoryVisible: function(categoryName)
100 elementsToRestoreScrollPositionsFor: function()
102 return [this.containerElement];
107 WebInspector.Panel.prototype.resize.call(this);
108 this._dataGrid.updateWidths();
109 this._positionSummaryBar();
112 updateSidebarWidth: function(width)
114 if (!this._viewingResourceMode)
116 WebInspector.Panel.prototype.updateSidebarWidth.call(this, width);
117 if (this._summaryBarElement.parentElement === this.element)
118 this._summaryBarElement.style.width = width + "px";
121 updateMainViewWidth: function(width)
123 this._viewsContainerElement.style.left = width + "px";
126 handleShortcut: function(event)
128 if (this._viewingResourceMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
129 this._toggleGridMode();
130 event.handled = true;
134 _positionSummaryBar: function()
136 // Position the total bar.
138 var fillerRow = this._dataGrid.dataTableBody.lastChild;
139 if (this._summaryBarElement.parentElement !== this.element && fillerRow.offsetHeight > 0) {
140 // Glue status to bottom.
141 if (this._summaryBarRowNode) {
142 this._dataGrid.removeChild(this._summaryBarRowNode);
143 delete this._summaryBarRowNode;
145 this._summaryBarElement.addStyleClass("network-summary-bar-bottom");
146 this._summaryBarElement.style.setProperty("width", this.sidebarElement.offsetWidth + "px");
147 this.element.appendChild(this._summaryBarElement);
148 this._dataGrid.element.style.bottom = "20px";
152 if (!this._summaryBarRowNode && !fillerRow.offsetHeight) {
153 // Glue status to table.
154 this._summaryBarRowNode = new WebInspector.NetworkTotalGridNode(this._summaryBarElement);
155 this._summaryBarElement.removeStyleClass("network-summary-bar-bottom");
156 this._summaryBarElement.style.removeProperty("width");
157 this._dataGrid.appendChild(this._summaryBarRowNode);
158 this._dataGrid.element.style.bottom = 0;
163 _resetSummaryBar: function()
165 delete this._summaryBarRowNode;
166 this._summaryBarElement.parentElement.removeChild(this._summaryBarElement);
167 this._updateSummaryBar();
170 _createTimelineGrid: function()
172 this._timelineGrid = new WebInspector.TimelineGrid();
173 this._timelineGrid.element.addStyleClass("network-timeline-grid");
174 this._dataGrid.element.appendChild(this._timelineGrid.element);
177 _createTable: function()
179 var columns = {name: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}};
180 columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path"));
181 columns.name.sortable = true;
182 columns.name.width = "20%";
183 columns.name.disclosure = true;
185 columns.method.title = WebInspector.UIString("Method");
186 columns.method.sortable = true;
187 columns.method.width = "7%";
189 columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text"));
190 columns.status.sortable = true;
191 columns.status.width = "8%";
193 columns.type.title = WebInspector.UIString("Type");
194 columns.type.sortable = true;
195 columns.type.width = "10%";
197 columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Transfer"));
198 columns.size.sortable = true;
199 columns.size.width = "10%";
200 columns.size.aligned = "right";
202 columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency"));
203 columns.time.sortable = true;
204 columns.time.width = "10%";
205 columns.time.aligned = "right";
207 columns.timeline.title = "";
208 columns.timeline.sortable = false;
209 columns.timeline.width = "37%";
210 columns.timeline.sort = "ascending";
212 this._dataGrid = new WebInspector.DataGrid(columns);
213 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
214 this.containerElement.appendChild(this._dataGrid.element);
215 this._dataGrid.addEventListener("sorting changed", this._sortItems, this);
216 this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this);
218 this._patchTimelineHeader();
221 _makeHeaderFragment: function(title, subtitle)
223 var fragment = document.createDocumentFragment();
224 fragment.appendChild(document.createTextNode(title));
225 var subtitleDiv = document.createElement("div");
226 subtitleDiv.className = "network-header-subtitle";
227 subtitleDiv.textContent = subtitle;
228 fragment.appendChild(subtitleDiv);
232 _patchTimelineHeader: function()
234 var timelineSorting = document.createElement("select");
236 var option = document.createElement("option");
237 option.value = "startTime";
238 option.label = WebInspector.UIString("Timeline");
239 timelineSorting.appendChild(option);
241 option = document.createElement("option");
242 option.value = "startTime";
243 option.label = WebInspector.UIString("Start Time");
244 timelineSorting.appendChild(option);
246 option = document.createElement("option");
247 option.value = "responseTime";
248 option.label = WebInspector.UIString("Response Time");
249 timelineSorting.appendChild(option);
251 option = document.createElement("option");
252 option.value = "endTime";
253 option.label = WebInspector.UIString("End Time");
254 timelineSorting.appendChild(option);
256 option = document.createElement("option");
257 option.value = "duration";
258 option.label = WebInspector.UIString("Duration");
259 timelineSorting.appendChild(option);
261 option = document.createElement("option");
262 option.value = "latency";
263 option.label = WebInspector.UIString("Latency");
264 timelineSorting.appendChild(option);
266 var header = this._dataGrid.headerTableHeader("timeline");
267 header.replaceChild(timelineSorting, header.firstChild);
269 timelineSorting.addEventListener("click", function(event) { event.stopPropagation() }, false);
270 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
271 this._timelineSortSelector = timelineSorting;
274 _createSortingFunctions: function()
276 this._sortingFunctions = {};
277 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
278 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "method", false);
279 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode", false);
280 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType", false);
281 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
282 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", false);
283 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
284 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
285 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "endTime", false);
286 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "responseReceivedTime", false);
287 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", true);
288 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "latency", true);
290 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
291 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
293 this._calculators = {};
294 this._calculators.timeline = timeCalculator;
295 this._calculators.startTime = timeCalculator;
296 this._calculators.endTime = timeCalculator;
297 this._calculators.responseTime = timeCalculator;
298 this._calculators.duration = durationCalculator;
299 this._calculators.latency = durationCalculator;
302 _sortItems: function()
304 var columnIdentifier = this._dataGrid.sortColumnIdentifier;
305 if (columnIdentifier === "timeline") {
306 this._sortByTimeline();
309 var sortingFunction = this._sortingFunctions[columnIdentifier];
310 if (!sortingFunction)
313 this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending");
314 this._timelineSortSelector.selectedIndex = 0;
317 _sortByTimeline: function()
319 var selectedIndex = this._timelineSortSelector.selectedIndex;
321 selectedIndex = 1; // Sort by start time by default.
322 var selectedOption = this._timelineSortSelector[selectedIndex];
323 var value = selectedOption.value;
325 var sortingFunction = this._sortingFunctions[value];
326 this._dataGrid.sortNodes(sortingFunction);
327 this.calculator = this._calculators[value];
328 if (this.calculator.startAtZero)
329 this._timelineGrid.hideEventDividers();
331 this._timelineGrid.showEventDividers();
332 this._dataGrid.markColumnAsSortedBy("timeline", "ascending");
335 _createFilterStatusBarItems: function()
337 var filterBarElement = document.createElement("div");
338 filterBarElement.className = "scope-bar status-bar-item";
339 filterBarElement.id = "network-filter";
341 function createFilterElement(category, label)
343 var categoryElement = document.createElement("li");
344 categoryElement.category = category;
345 categoryElement.className = category;
346 categoryElement.appendChild(document.createTextNode(label));
347 categoryElement.addEventListener("click", this._updateFilter.bind(this), false);
348 filterBarElement.appendChild(categoryElement);
350 return categoryElement;
353 this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
356 var dividerElement = document.createElement("div");
357 dividerElement.addStyleClass("scope-bar-divider");
358 filterBarElement.appendChild(dividerElement);
360 for (var category in this._categories)
361 createFilterElement.call(this, category, this._categories[category].title);
362 this._filterBarElement = filterBarElement;
365 _createSummaryBar: function()
367 this._summaryBarElement = document.createElement("div");
368 this._summaryBarElement.className = "network-summary-bar";
369 this.containerElement.appendChild(this._summaryBarElement);
372 _updateSummaryBar: function()
374 this._positionSummaryBar(); // Grid is growing.
375 var numRequests = this._resources.length;
378 if (this._summaryBarElement._isDisplayingWarning)
380 this._summaryBarElement._isDisplayingWarning = true;
382 var img = document.createElement("img");
383 img.src = "Images/warningIcon.png";
384 this._summaryBarElement.removeChildren();
385 this._summaryBarElement.appendChild(img);
386 this._summaryBarElement.appendChild(document.createTextNode(" "));
387 this._summaryBarElement.appendChild(document.createTextNode(
388 WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
391 delete this._summaryBarElement._isDisplayingWarning;
393 var transferSize = 0;
396 for (var i = 0; i < this._resources.length; ++i) {
397 var resource = this._resources[i];
398 transferSize += (resource.cached || !resource.transferSize) ? 0 : resource.transferSize;
399 if (resource.isMainResource)
400 baseTime = resource.startTime;
401 if (resource.endTime > maxTime)
402 maxTime = resource.endTime;
404 var text = String.sprintf(WebInspector.UIString("%d requests"), numRequests);
405 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
406 if (baseTime !== -1 && this._mainResourceLoadTime !== -1 && this._mainResourceDOMContentTime !== -1 && this._mainResourceDOMContentTime > baseTime) {
407 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
408 Number.secondsToString(maxTime - baseTime),
409 Number.secondsToString(this._mainResourceLoadTime - baseTime),
410 Number.secondsToString(this._mainResourceDOMContentTime - baseTime));
412 this._summaryBarElement.textContent = text;
415 _showCategory: function(category)
417 this._dataGrid.element.addStyleClass("filter-" + category);
418 delete this._hiddenCategories[category];
421 _hideCategory: function(category)
423 this._dataGrid.element.removeStyleClass("filter-" + category);
424 this._hiddenCategories[category] = true;
427 _updateFilter: function(e)
429 var isMac = WebInspector.isMac();
430 var selectMultiple = false;
431 if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
432 selectMultiple = true;
433 if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
434 selectMultiple = true;
436 this._filter(e.target, selectMultiple);
437 this._positionSummaryBar();
440 _filter: function(target, selectMultiple)
442 function unselectAll()
444 for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) {
445 var child = this._filterBarElement.childNodes[i];
449 child.removeStyleClass("selected");
450 this._hideCategory(child.category);
454 if (target.category === this._filterAllElement) {
455 if (target.hasStyleClass("selected")) {
456 // We can't unselect All, so we break early here
460 // If All wasn't selected, and now is, unselect everything else.
461 unselectAll.call(this);
463 // Something other than All is being selected, so we want to unselect All.
464 if (this._filterAllElement.hasStyleClass("selected")) {
465 this._filterAllElement.removeStyleClass("selected");
466 this._hideCategory("all");
470 if (!selectMultiple) {
471 // If multiple selection is off, we want to unselect everything else
472 // and just select ourselves.
473 unselectAll.call(this);
475 target.addStyleClass("selected");
476 this._showCategory(target.category);
480 if (target.hasStyleClass("selected")) {
481 // If selectMultiple is turned on, and we were selected, we just
482 // want to unselect ourselves.
483 target.removeStyleClass("selected");
484 this._hideCategory(target.category);
486 // If selectMultiple is turned on, and we weren't selected, we just
487 // want to select ourselves.
488 target.addStyleClass("selected");
489 this._showCategory(target.category);
493 _scheduleRefresh: function()
495 if (this._needsRefresh)
498 this._needsRefresh = true;
500 if (this.visible && !("_refreshTimeout" in this))
501 this._refreshTimeout = setTimeout(this.refresh.bind(this), 500);
504 _updateDividersIfNeeded: function(force)
506 var timelineColumn = this._dataGrid.columns.timeline;
507 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
508 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnID) {
509 // Position timline grid location.
510 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
511 this._timelineGrid.element.style.right = "18px";
517 this._scheduleRefresh();
520 proceed = this._timelineGrid.updateDividers(force, this.calculator);
525 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
526 // If our current sorting method starts at zero, that means it shows all
527 // resources starting at the same point, and so onLoad event and DOMContent
528 // event lines really wouldn't make much sense here, so don't render them.
529 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
530 // function defined, we are probably sorting by size, and event times aren't relevant
535 this._timelineGrid.removeEventDividers();
536 if (this._mainResourceLoadTime !== -1) {
537 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceLoadTime);
539 var loadDivider = document.createElement("div");
540 loadDivider.className = "network-event-divider network-red-divider";
542 var loadDividerPadding = document.createElement("div");
543 loadDividerPadding.className = "network-event-divider-padding";
544 loadDividerPadding.title = WebInspector.UIString("Load event fired");
545 loadDividerPadding.appendChild(loadDivider);
546 loadDividerPadding.style.left = percent + "%";
547 this._timelineGrid.addEventDivider(loadDividerPadding);
550 if (this._mainResourceDOMContentTime !== -1) {
551 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceDOMContentTime);
553 var domContentDivider = document.createElement("div");
554 domContentDivider.className = "network-event-divider network-blue-divider";
556 var domContentDividerPadding = document.createElement("div");
557 domContentDividerPadding.className = "network-event-divider-padding";
558 domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
559 domContentDividerPadding.appendChild(domContentDivider);
560 domContentDividerPadding.style.left = percent + "%";
561 this._timelineGrid.addEventDivider(domContentDividerPadding);
565 _refreshIfNeeded: function()
567 if (this._needsRefresh)
571 _invalidateAllItems: function()
573 this._staleResources = this._resources.slice();
578 return this._calculator;
583 if (!x || this._calculator === x)
586 this._calculator = x;
587 this._calculator.reset();
589 this._invalidateAllItems();
593 _resourceGridNode: function(resource)
595 return this._resourceGridNodes[resource.identifier];
598 revealAndSelectItem: function(resource)
600 var node = this._resourceGridNode(resource);
607 addEventDivider: function(divider)
609 this._timelineGrid.addEventDivider(divider);
612 _createStatusbarButtons: function()
614 this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
615 this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked.bind(this), false);
617 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
618 this._clearButton.addEventListener("click", this._reset.bind(this), false);
620 this._largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
621 this._largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows;
622 this._largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
625 set mainResourceLoadTime(x)
627 if (this._mainResourceLoadTime === x)
630 this._mainResourceLoadTime = x || -1;
631 // Update the dividers to draw the new line
632 this._updateDividersIfNeeded(true);
635 set mainResourceDOMContentTime(x)
637 if (this._mainResourceDOMContentTime === x)
640 this._mainResourceDOMContentTime = x || -1;
641 this._updateDividersIfNeeded(true);
646 WebInspector.Panel.prototype.show.call(this);
647 this._refreshIfNeeded();
649 if (this.visibleView)
650 this.visibleView.show(this._viewsContainerElement);
652 this._dataGrid.updateWidths();
653 this._positionSummaryBar();
658 WebInspector.Panel.prototype.hide.call(this);
659 this._popoverHelper.hidePopup();
662 get searchableViews()
668 searchMatchFound: function(view, matches)
670 this._resourceGridNode(view.resource).searchMatches = matches;
673 searchCanceled: function(startingNewSearch)
675 WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
677 if (startingNewSearch || !this._resources)
681 performSearch: function(query)
683 WebInspector.Panel.prototype.performSearch.call(this, query);
688 this._needsRefresh = false;
689 if ("_refreshTimeout" in this) {
690 clearTimeout(this._refreshTimeout);
691 delete this._refreshTimeout;
694 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
695 var staleItemsLength = this._staleResources.length;
696 var boundariesChanged = false;
698 for (var i = 0; i < staleItemsLength; ++i) {
699 var resource = this._staleResources[i];
700 var node = this._resourceGridNode(resource);
702 // Create the timeline tree element and graph.
703 node = new WebInspector.NetworkDataGridNode(this, resource);
704 this._resourceGridNodes[resource.identifier] = node;
705 this._dataGrid.appendChild(node);
707 node.refreshResource();
709 if (this.calculator.updateBoundaries(resource))
710 boundariesChanged = true;
713 if (boundariesChanged) {
714 // The boundaries changed, so all item graphs are stale.
715 this._invalidateAllItems();
716 staleItemsLength = this._staleResources.length;
719 for (var i = 0; i < staleItemsLength; ++i)
720 this._resourceGridNode(this._staleResources[i]).refreshGraph(this.calculator);
722 this._staleResources = [];
724 this._updateSummaryBar();
725 this._dataGrid.updateWidths();
727 if (wasScrolledToLastRow)
728 this._dataGrid.scrollToLastRow();
731 _onPreserveLogClicked: function(e)
733 this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
738 if (!this._preserveLogToggle.toggled)
744 this._popoverHelper.hidePopup();
745 this._closeVisibleResource();
747 this._toggleGridMode();
749 // Begin reset timeline
750 if (this._calculator)
751 this._calculator.reset();
753 this._resources = [];
754 this._resourcesById = {};
755 this._resourcesByURL = {};
756 this._staleResources = [];
757 this._resourceGridNodes = {};
759 this._dataGrid.removeChildren();
760 delete this._summaryBarRowNode;
761 this._updateDividersIfNeeded(true);
762 // End reset timeline.
764 this._mainResourceLoadTime = -1;
765 this._mainResourceDOMContentTime = -1;
767 this._viewsContainerElement.removeChildren();
768 this._viewsContainerElement.appendChild(this._closeButtonElement);
769 this._resetSummaryBar();
774 return this._resourcesById;
777 refreshResource: function(resource)
779 if (!resource.identifier)
780 resource.identifier = "network:" + this._lastIdentifier++;
782 if (!this._resourcesById[resource.identifier]) {
783 this._resources.push(resource);
784 this._resourcesById[resource.identifier] = resource;
785 this._resourcesByURL[resource.url] = resource;
787 // Pull all the redirects of the main resource upon commit load.
788 if (resource.redirects) {
789 for (var i = 0; i < resource.redirects.length; ++i)
790 this.refreshResource(resource.redirects[i]);
794 this._staleResources.push(resource);
795 this._scheduleRefresh();
800 var oldView = WebInspector.ResourceView.existingResourceViewForResource(resource);
804 if (WebInspector.ResourceView.resourceViewTypeMatchesResource(resource))
807 var newView = WebInspector.ResourceView.recreateResourceView(resource);
808 if (this.visibleView === oldView)
809 this.visibleView = newView;
812 canShowSourceLine: function(url, line)
814 return !!this._resourcesByURL[url];
817 showSourceLine: function(url, line)
819 this._showResource(this._resourcesByURL[url], line);
822 _showResource: function(resource, line)
827 this._popoverHelper.hidePopup();
829 this._toggleViewingResourceMode();
831 if (this.visibleView) {
832 this.visibleView.detach();
833 delete this.visibleView;
836 var view = new WebInspector.NetworkItemView(resource);
837 view.show(this._viewsContainerElement);
838 this.visibleView = view;
840 this.updateSidebarWidth();
843 _closeVisibleResource: function()
845 this.element.removeStyleClass("viewing-resource");
847 if (this.visibleView) {
848 this.visibleView.detach();
849 delete this.visibleView;
852 if (this._lastSelectedGraphTreeElement)
853 this._lastSelectedGraphTreeElement.select(true);
855 this.updateSidebarWidth();
858 _toggleLargerResources: function()
860 WebInspector.settings.resourcesLargeRows = !WebInspector.settings.resourcesLargeRows;
861 this._setLargerResources(WebInspector.settings.resourcesLargeRows);
864 _setLargerResources: function(enabled)
866 this._largerResourcesButton.toggled = enabled;
868 this._largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
869 this._dataGrid.element.addStyleClass("small");
870 this._timelineGrid.element.addStyleClass("small");
871 this._viewsContainerElement.addStyleClass("small");
873 this._largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
874 this._dataGrid.element.removeStyleClass("small");
875 this._timelineGrid.element.removeStyleClass("small");
876 this._viewsContainerElement.removeStyleClass("small");
878 this._positionSummaryBar();
881 _getPopoverAnchor: function(element)
883 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
886 var resource = anchor.parentElement.resource;
887 return resource && resource.timing ? anchor : null;
890 _showPopover: function(anchor)
892 var resource = anchor.parentElement.resource;
893 var tableElement = WebInspector.ResourceTimingView.createTimingTable(resource);
894 var popover = new WebInspector.Popover(tableElement);
895 popover.show(anchor);
899 _toggleGridMode: function()
901 if (this._viewingResourceMode) {
902 this._viewingResourceMode = false;
903 this.element.removeStyleClass("viewing-resource");
904 this._dataGrid.element.removeStyleClass("viewing-resource-mode");
905 this._viewsContainerElement.addStyleClass("hidden");
906 this.sidebarElement.style.right = 0;
907 this.sidebarElement.style.removeProperty("width");
908 this._summaryBarElement.style.removeProperty("width");
909 if (this._dataGrid.selectedNode)
910 this._dataGrid.selectedNode.selected = false;
913 if (this._briefGrid) {
914 this._dataGrid.element.removeStyleClass("full-grid-mode");
915 this._dataGrid.element.addStyleClass("brief-grid-mode");
917 this._dataGrid.hideColumn("method");
918 this._dataGrid.hideColumn("status");
919 this._dataGrid.hideColumn("type");
920 this._dataGrid.hideColumn("size");
921 this._dataGrid.hideColumn("time");
925 widths.timeline = 80;
927 this._dataGrid.element.addStyleClass("full-grid-mode");
928 this._dataGrid.element.removeStyleClass("brief-grid-mode");
930 this._dataGrid.showColumn("method");
931 this._dataGrid.showColumn("status");
932 this._dataGrid.showColumn("type");
933 this._dataGrid.showColumn("size");
934 this._dataGrid.showColumn("time");
943 widths.timeline = 37;
946 this._dataGrid.showColumn("timeline");
947 this._dataGrid.applyColumnWidthsMap(widths);
951 _toggleViewingResourceMode: function()
953 if (this._viewingResourceMode)
955 this._viewingResourceMode = true;
956 this._preservedColumnWidths = this._dataGrid.columnWidthsMap();
958 this.element.addStyleClass("viewing-resource");
959 this._dataGrid.element.addStyleClass("viewing-resource-mode");
960 this._dataGrid.element.removeStyleClass("full-grid-mode");
961 this._dataGrid.element.removeStyleClass("brief-grid-mode");
963 this._dataGrid.hideColumn("method");
964 this._dataGrid.hideColumn("status");
965 this._dataGrid.hideColumn("type");
966 this._dataGrid.hideColumn("size");
967 this._dataGrid.hideColumn("time");
968 this._dataGrid.hideColumn("timeline");
970 this._viewsContainerElement.removeStyleClass("hidden");
971 this.updateSidebarWidth(200);
975 this._dataGrid.applyColumnWidthsMap(widths);
978 _contextMenu: function(event)
980 // createBlobURL is enabled conditionally, do not expose resource export if it's not available.
981 if (typeof window.createObjectURL !== "function" || !Preferences.resourceExportEnabled)
984 var contextMenu = new WebInspector.ContextMenu();
985 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
986 var resource = gridNode && gridNode._resource;
988 contextMenu.appendItem(WebInspector.UIString("Export to HAR"), this._exportResource.bind(this, resource));
989 contextMenu.appendItem(WebInspector.UIString("Export all to HAR"), this._exportAll.bind(this));
990 contextMenu.show(event);
993 _exportAll: function()
996 log: (new WebInspector.HARLog()).build()
998 offerFileForDownload(JSON.stringify(harArchive));
1001 _exportResource: function(resource)
1003 var har = (new WebInspector.HAREntry(resource)).build();
1004 offerFileForDownload(JSON.stringify(har));
1008 WebInspector.NetworkPanel.prototype.__proto__ = WebInspector.Panel.prototype;
1010 WebInspector.NetworkBaseCalculator = function()
1014 WebInspector.NetworkBaseCalculator.prototype = {
1015 computeSummaryValues: function(items)
1018 var categoryValues = {};
1020 var itemsLength = items.length;
1021 for (var i = 0; i < itemsLength; ++i) {
1022 var item = items[i];
1023 var value = this._value(item);
1024 if (typeof value === "undefined")
1026 if (!(item.category.name in categoryValues))
1027 categoryValues[item.category.name] = 0;
1028 categoryValues[item.category.name] += value;
1032 return {categoryValues: categoryValues, total: total};
1035 computeBarGraphPercentages: function(item)
1037 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan) * 100};
1040 computeBarGraphLabels: function(item)
1042 const label = this.formatValue(this._value(item));
1043 return {left: label, right: label, tooltip: label};
1048 return this.maximumBoundary - this.minimumBoundary;
1051 updateBoundaries: function(item)
1053 this.minimumBoundary = 0;
1055 var value = this._value(item);
1056 if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) {
1057 this.maximumBoundary = value;
1065 delete this.minimumBoundary;
1066 delete this.maximumBoundary;
1069 _value: function(item)
1074 formatValue: function(value)
1076 return value.toString();
1080 WebInspector.NetworkTimeCalculator = function(startAtZero)
1082 WebInspector.NetworkBaseCalculator.call(this);
1083 this.startAtZero = startAtZero;
1086 WebInspector.NetworkTimeCalculator.prototype = {
1087 computeSummaryValues: function(resources)
1089 var resourcesByCategory = {};
1090 var resourcesLength = resources.length;
1091 for (var i = 0; i < resourcesLength; ++i) {
1092 var resource = resources[i];
1093 if (!(resource.category.name in resourcesByCategory))
1094 resourcesByCategory[resource.category.name] = [];
1095 resourcesByCategory[resource.category.name].push(resource);
1100 var categoryValues = {};
1101 for (var category in resourcesByCategory) {
1102 resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
1103 categoryValues[category] = 0;
1105 var segment = {start: -1, end: -1};
1107 var categoryResources = resourcesByCategory[category];
1108 var resourcesLength = categoryResources.length;
1109 for (var i = 0; i < resourcesLength; ++i) {
1110 var resource = categoryResources[i];
1111 if (resource.startTime === -1 || resource.endTime === -1)
1114 if (typeof earliestStart === "undefined")
1115 earliestStart = resource.startTime;
1117 earliestStart = Math.min(earliestStart, resource.startTime);
1119 if (typeof latestEnd === "undefined")
1120 latestEnd = resource.endTime;
1122 latestEnd = Math.max(latestEnd, resource.endTime);
1124 if (resource.startTime <= segment.end) {
1125 segment.end = Math.max(segment.end, resource.endTime);
1129 categoryValues[category] += segment.end - segment.start;
1131 segment.start = resource.startTime;
1132 segment.end = resource.endTime;
1135 // Add the last segment
1136 categoryValues[category] += segment.end - segment.start;
1139 return {categoryValues: categoryValues, total: latestEnd - earliestStart};
1142 computeBarGraphPercentages: function(resource)
1144 if (resource.startTime !== -1)
1145 var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
1149 if (resource.responseReceivedTime !== -1)
1150 var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
1152 var middle = (this.startAtZero ? start : 100);
1154 if (resource.endTime !== -1)
1155 var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
1157 var end = (this.startAtZero ? middle : 100);
1159 if (this.startAtZero) {
1165 return {start: start, middle: middle, end: end};
1168 computePercentageFromEventTime: function(eventTime)
1170 // This function computes a percentage in terms of the total loading time
1171 // of a specific event. If startAtZero is set, then this is useless, and we
1172 // want to return 0.
1173 if (eventTime !== -1 && !this.startAtZero)
1174 return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100;
1179 computeBarGraphLabels: function(resource)
1181 var rightLabel = "";
1182 if (resource.responseReceivedTime !== -1 && resource.endTime !== -1)
1183 rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime);
1185 var hasLatency = resource.latency > 0;
1187 var leftLabel = this.formatValue(resource.latency);
1189 var leftLabel = rightLabel;
1191 if (resource.timing)
1192 return {left: leftLabel, right: rightLabel};
1194 if (hasLatency && rightLabel) {
1195 var total = this.formatValue(resource.duration);
1196 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
1197 } else if (hasLatency)
1198 var tooltip = WebInspector.UIString("%s latency", leftLabel);
1199 else if (rightLabel)
1200 var tooltip = WebInspector.UIString("%s download", rightLabel);
1202 if (resource.cached)
1203 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
1204 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
1207 updateBoundaries: function(resource)
1209 var didChange = false;
1212 if (this.startAtZero)
1215 lowerBound = this._lowerBound(resource);
1217 if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
1218 this.minimumBoundary = lowerBound;
1222 var upperBound = this._upperBound(resource);
1223 if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
1224 this.maximumBoundary = upperBound;
1231 formatValue: function(value)
1233 return Number.secondsToString(value, WebInspector.UIString);
1236 _lowerBound: function(resource)
1241 _upperBound: function(resource)
1247 WebInspector.NetworkTimeCalculator.prototype.__proto__ = WebInspector.NetworkBaseCalculator.prototype;
1249 WebInspector.NetworkTransferTimeCalculator = function()
1251 WebInspector.NetworkTimeCalculator.call(this, false);
1254 WebInspector.NetworkTransferTimeCalculator.prototype = {
1255 formatValue: function(value)
1257 return Number.secondsToString(value, WebInspector.UIString);
1260 _lowerBound: function(resource)
1262 return resource.startTime;
1265 _upperBound: function(resource)
1267 return resource.endTime;
1271 WebInspector.NetworkTransferTimeCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1273 WebInspector.NetworkTransferDurationCalculator = function()
1275 WebInspector.NetworkTimeCalculator.call(this, true);
1278 WebInspector.NetworkTransferDurationCalculator.prototype = {
1279 formatValue: function(value)
1281 return Number.secondsToString(value, WebInspector.UIString);
1284 _upperBound: function(resource)
1286 return resource.duration;
1290 WebInspector.NetworkTransferDurationCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1292 WebInspector.NetworkDataGridNode = function(panel, resource)
1294 WebInspector.DataGridNode.call(this, {});
1295 this._panel = panel;
1296 this._resource = resource;
1299 WebInspector.NetworkDataGridNode.prototype = {
1300 createCells: function()
1302 this._nameCell = this._createDivInTD("name");
1303 this._methodCell = this._createDivInTD("method");
1304 this._statusCell = this._createDivInTD("status");
1305 this._typeCell = this._createDivInTD("type");
1306 this._sizeCell = this._createDivInTD("size");
1307 this._timeCell = this._createDivInTD("time");
1308 this._createTimelineCell();
1309 this._nameCell.addEventListener("click", this.select.bind(this), false);
1314 this._panel._showResource(this._resource);
1315 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
1320 if (!this._panel._viewingResourceMode)
1322 if (!this._panel._hiddenCategories.all)
1324 if (this._panel._hiddenCategories[this._resource.category.name])
1329 _createDivInTD: function(columnIdentifier)
1331 var td = document.createElement("td");
1332 td.className = columnIdentifier + "-column";
1333 var div = document.createElement("div");
1334 td.appendChild(div);
1335 this._element.appendChild(td);
1339 _createTimelineCell: function()
1341 this._graphElement = document.createElement("div");
1342 this._graphElement.className = "network-graph-side";
1344 this._barAreaElement = document.createElement("div");
1345 // this._barAreaElement.className = "network-graph-bar-area hidden";
1346 this._barAreaElement.className = "network-graph-bar-area";
1347 this._barAreaElement.resource = this._resource;
1348 this._graphElement.appendChild(this._barAreaElement);
1350 this._barLeftElement = document.createElement("div");
1351 this._barLeftElement.className = "network-graph-bar waiting";
1352 this._barAreaElement.appendChild(this._barLeftElement);
1354 this._barRightElement = document.createElement("div");
1355 this._barRightElement.className = "network-graph-bar";
1356 this._barAreaElement.appendChild(this._barRightElement);
1359 this._labelLeftElement = document.createElement("div");
1360 this._labelLeftElement.className = "network-graph-label waiting";
1361 this._barAreaElement.appendChild(this._labelLeftElement);
1363 this._labelRightElement = document.createElement("div");
1364 this._labelRightElement.className = "network-graph-label";
1365 this._barAreaElement.appendChild(this._labelRightElement);
1367 this._graphElement.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
1369 this._timelineCell = document.createElement("td");
1370 this._timelineCell.className = "timeline-column";
1371 this._element.appendChild(this._timelineCell);
1372 this._timelineCell.appendChild(this._graphElement);
1375 refreshResource: function()
1377 this._refreshNameCell();
1379 this._methodCell.textContent = this._resource.requestMethod;
1381 this._refreshStatusCell();
1383 if (this._resource.mimeType) {
1384 this._typeCell.removeStyleClass("network-dim-cell");
1385 this._typeCell.textContent = this._resource.mimeType;
1387 this._typeCell.addStyleClass("network-dim-cell");
1388 this._typeCell.textContent = WebInspector.UIString("Pending");
1391 this._refreshSizeCell();
1392 this._refreshTimeCell();
1394 if (this._resource.cached)
1395 this._graphElement.addStyleClass("resource-cached");
1397 this._element.addStyleClass("network-item");
1398 if (!this._element.hasStyleClass("network-category-" + this._resource.category.name)) {
1399 this._element.removeMatchingStyleClasses("network-category-\\w+");
1400 this._element.addStyleClass("network-category-" + this._resource.category.name);
1404 _refreshNameCell: function()
1406 this._nameCell.removeChildren();
1408 if (this._resource.category === WebInspector.resourceCategories.images) {
1409 var previewImage = document.createElement("img");
1410 previewImage.className = "image-network-icon-preview";
1412 function onResourceContent()
1414 previewImage.src = this._resource.contentURL;
1416 if (Preferences.useDataURLForResourceImageIcons)
1417 this._resource.requestContent(onResourceContent.bind(this));
1419 previewImage.src = this._resource.url;
1421 var iconElement = document.createElement("div");
1422 iconElement.className = "icon";
1423 iconElement.appendChild(previewImage);
1425 var iconElement = document.createElement("img");
1426 iconElement.className = "icon";
1428 this._nameCell.appendChild(iconElement);
1429 this._nameCell.appendChild(document.createTextNode(this._fileName()));
1432 var subtitle = this._resource.displayDomain;
1434 if (this._resource.path && this._resource.lastPathComponent) {
1435 var lastPathComponentIndex = this._resource.path.lastIndexOf("/" + this._resource.lastPathComponent);
1436 if (lastPathComponentIndex != -1)
1437 subtitle += this._resource.path.substring(0, lastPathComponentIndex);
1440 this._appendSubtitle(this._nameCell, subtitle);
1441 this._nameCell.title = this._resource.url;
1444 _fileName: function()
1446 var fileName = this._resource.displayName;
1447 if (this._resource.queryString)
1448 fileName += "?" + this._resource.queryString;
1452 _refreshStatusCell: function()
1454 this._statusCell.removeChildren();
1456 var fromCache = this._resource.cached;
1458 this._statusCell.textContent = WebInspector.UIString("(from cache)");
1459 this._statusCell.addStyleClass("network-dim-cell");
1463 this._statusCell.removeStyleClass("network-dim-cell");
1464 if (this._resource.statusCode) {
1465 this._statusCell.appendChild(document.createTextNode(this._resource.statusCode));
1466 this._statusCell.removeStyleClass("network-dim-cell");
1467 this._appendSubtitle(this._statusCell, this._resource.statusText);
1468 this._statusCell.title = this._resource.statusCode + " " + this._resource.statusText;
1470 this._statusCell.addStyleClass("network-dim-cell");
1471 this._statusCell.textContent = WebInspector.UIString("Pending");
1475 _refreshSizeCell: function()
1477 var resourceSize = typeof this._resource.resourceSize === "number" ? Number.bytesToString(this._resource.resourceSize) : "?";
1478 var transferSize = typeof this._resource.transferSize === "number" ? Number.bytesToString(this._resource.transferSize) : "?";
1479 var fromCache = this._resource.cached;
1480 this._sizeCell.textContent = !fromCache ? resourceSize : WebInspector.UIString("(from cache)");
1482 this._sizeCell.addStyleClass("network-dim-cell");
1484 this._sizeCell.removeStyleClass("network-dim-cell");
1486 this._appendSubtitle(this._sizeCell, transferSize);
1489 _refreshTimeCell: function()
1491 if (this._resource.duration > 0) {
1492 this._timeCell.removeStyleClass("network-dim-cell");
1493 this._timeCell.textContent = Number.secondsToString(this._resource.duration);
1494 this._appendSubtitle(this._timeCell, Number.secondsToString(this._resource.latency));
1496 this._timeCell.addStyleClass("network-dim-cell");
1497 this._timeCell.textContent = WebInspector.UIString("Pending");
1501 _appendSubtitle: function(cellElement, subtitleText)
1503 var subtitleElement = document.createElement("div");
1504 subtitleElement.className = "network-cell-subtitle";
1505 subtitleElement.textContent = subtitleText;
1506 cellElement.appendChild(subtitleElement);
1509 refreshGraph: function(calculator)
1511 var percentages = calculator.computeBarGraphPercentages(this._resource);
1512 this._percentages = percentages;
1514 this._barAreaElement.removeStyleClass("hidden");
1516 if (!this._graphElement.hasStyleClass("network-category-" + this._resource.category.name)) {
1517 this._graphElement.removeMatchingStyleClasses("network-category-\\w+");
1518 this._graphElement.addStyleClass("network-category-" + this._resource.category.name);
1521 this._barLeftElement.style.setProperty("left", percentages.start + "%");
1522 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
1524 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
1525 this._barRightElement.style.setProperty("left", percentages.middle + "%");
1527 var labels = calculator.computeBarGraphLabels(this._resource);
1528 this._labelLeftElement.textContent = labels.left;
1529 this._labelRightElement.textContent = labels.right;
1531 var tooltip = (labels.tooltip || "");
1532 this._barLeftElement.title = tooltip;
1533 this._labelLeftElement.title = tooltip;
1534 this._labelRightElement.title = tooltip;
1535 this._barRightElement.title = tooltip;
1538 _refreshLabelPositions: function()
1540 if (!this._percentages)
1542 this._labelLeftElement.style.removeProperty("left");
1543 this._labelLeftElement.style.removeProperty("right");
1544 this._labelLeftElement.removeStyleClass("before");
1545 this._labelLeftElement.removeStyleClass("hidden");
1547 this._labelRightElement.style.removeProperty("left");
1548 this._labelRightElement.style.removeProperty("right");
1549 this._labelRightElement.removeStyleClass("after");
1550 this._labelRightElement.removeStyleClass("hidden");
1552 const labelPadding = 10;
1553 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
1554 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
1556 if (this._barLeftElement) {
1557 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
1558 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
1560 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
1561 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
1564 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
1565 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
1567 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
1568 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
1569 const graphElementOffsetWidth = this._graphElement.offsetWidth;
1571 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
1572 var leftHidden = true;
1574 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
1575 var rightHidden = true;
1577 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
1578 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
1579 if (labelBefore && !labelAfter)
1581 else if (labelAfter && !labelBefore)
1587 this._labelLeftElement.addStyleClass("hidden");
1588 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
1589 this._labelLeftElement.addStyleClass("before");
1591 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
1592 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
1597 this._labelRightElement.addStyleClass("hidden");
1598 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
1599 this._labelRightElement.addStyleClass("after");
1601 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
1602 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
1607 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
1609 var aFileName = a._resource.displayName + (a._resource.queryString ? a._resource.queryString : "");
1610 var bFileName = b._resource.displayName + (b._resource.queryString ? b._resource.queryString : "");
1611 if (aFileName > bFileName)
1613 if (bFileName > aFileName)
1618 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
1620 if (b._resource.cached && !a._resource.cached)
1622 if (a._resource.cached && !b._resource.cached)
1625 if (a._resource.resourceSize === b._resource.resourceSize)
1628 return a._resource.resourceSize - b._resource.resourceSize;
1631 WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyName, revert, a, b)
1633 var aValue = a._resource[propertyName];
1634 var bValue = b._resource[propertyName];
1635 if (aValue > bValue)
1636 return revert ? -1 : 1;
1637 if (bValue > aValue)
1638 return revert ? 1 : -1;
1642 WebInspector.NetworkDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
1644 WebInspector.NetworkTotalGridNode = function(element)
1646 this._summaryBarElement = element;
1647 WebInspector.DataGridNode.call(this, {summaryRow: true});
1650 WebInspector.NetworkTotalGridNode.prototype = {
1651 createCells: function()
1653 var td = document.createElement("td");
1654 td.setAttribute("colspan", 7);
1655 td.className = "network-summary";
1656 td.appendChild(this._summaryBarElement);
1657 this._element.appendChild(td);
1661 WebInspector.NetworkTotalGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;