2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4 * Copyright (C) 2011 Google Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 WebInspector.NetworkPanel = function()
33 WebInspector.Panel.call(this, "network");
36 this.sidebarElement.className = "network-sidebar";
39 this._resourcesById = {};
40 this._resourcesByURL = {};
41 this._staleResources = [];
42 this._resourceGridNodes = {};
43 this._mainResourceLoadTime = -1;
44 this._mainResourceDOMContentTime = -1;
45 this._hiddenCategories = {};
47 this._categories = WebInspector.resourceCategories;
49 this.containerElement = document.createElement("div");
50 this.containerElement.id = "network-container";
51 this.sidebarElement.appendChild(this.containerElement);
53 this._viewsContainerElement = document.createElement("div");
54 this._viewsContainerElement.id = "network-views";
55 this._viewsContainerElement.className = "hidden";
56 this.element.appendChild(this._viewsContainerElement);
58 this._closeButtonElement = document.createElement("button");
59 this._closeButtonElement.id = "network-close-button";
60 this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
61 this._viewsContainerElement.appendChild(this._closeButtonElement);
63 this._createSortingFunctions();
65 this._createTimelineGrid();
66 this._createStatusbarButtons();
67 this._createFilterStatusBarItems();
68 this._createSummaryBar();
70 if (!WebInspector.settings.resourcesLargeRows)
71 this._setLargerResources(WebInspector.settings.resourcesLargeRows);
73 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), true);
74 // Enable faster hint.
75 this._popoverHelper.setTimeout(100);
77 this.calculator = new WebInspector.NetworkTransferTimeCalculator();
78 this._filter(this._filterAllElement, false);
80 this._toggleGridMode();
82 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
83 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this);
84 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceUpdated, this);
85 WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.MainResourceCommitLoad, this._onMainResourceCommitLoad, this);
88 WebInspector.NetworkPanel.prototype = {
89 get toolbarItemLabel()
91 return WebInspector.UIString("Network");
96 return [this._largerResourcesButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement];
99 isCategoryVisible: function(categoryName)
104 elementsToRestoreScrollPositionsFor: function()
106 return [this.containerElement, this._dataGrid.scrollContainer];
111 WebInspector.Panel.prototype.resize.call(this);
112 this._dataGrid.updateWidths();
113 this._positionSummaryBar();
116 updateSidebarWidth: function(width)
118 if (!this._viewingResourceMode)
120 WebInspector.Panel.prototype.updateSidebarWidth.call(this, width);
123 updateMainViewWidth: function(width)
125 this._viewsContainerElement.style.left = width + "px";
128 handleShortcut: function(event)
130 if (this._viewingResourceMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
131 this._toggleGridMode();
132 event.handled = true;
136 _positionSummaryBar: function()
138 // Position the total bar.
140 var fillerRow = this._dataGrid.dataTableBody.lastChild;
141 if (this._summaryBarElement.parentElement !== this.element && fillerRow.offsetHeight > 0) {
142 // Glue status to bottom.
143 if (this._summaryBarRowNode) {
144 this._dataGrid.removeChild(this._summaryBarRowNode);
145 delete this._summaryBarRowNode;
147 this._summaryBarElement.addStyleClass("network-summary-bar-bottom");
148 this.element.appendChild(this._summaryBarElement);
149 this._dataGrid.element.style.bottom = "20px";
153 if (!this._summaryBarRowNode && !fillerRow.offsetHeight) {
154 // Glue status to table.
155 this._summaryBarRowNode = new WebInspector.NetworkTotalGridNode(this._summaryBarElement);
156 this._summaryBarElement.removeStyleClass("network-summary-bar-bottom");
157 this._dataGrid.appendChild(this._summaryBarRowNode);
158 this._dataGrid.element.style.bottom = 0;
161 this._updateOffscreenRows();
164 _resetSummaryBar: function()
166 delete this._summaryBarRowNode;
167 this._summaryBarElement.parentElement.removeChild(this._summaryBarElement);
168 this._updateSummaryBar();
171 _createTimelineGrid: function()
173 this._timelineGrid = new WebInspector.TimelineGrid();
174 this._timelineGrid.element.addStyleClass("network-timeline-grid");
175 this._dataGrid.element.appendChild(this._timelineGrid.element);
178 _createTable: function()
180 var columns = {name: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}};
181 columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path"));
182 columns.name.sortable = true;
183 columns.name.width = "20%";
184 columns.name.disclosure = true;
186 columns.method.title = WebInspector.UIString("Method");
187 columns.method.sortable = true;
188 columns.method.width = "7%";
190 columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text"));
191 columns.status.sortable = true;
192 columns.status.width = "8%";
194 columns.type.title = WebInspector.UIString("Type");
195 columns.type.sortable = true;
196 columns.type.width = "10%";
198 columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Transfer"));
199 columns.size.sortable = true;
200 columns.size.width = "10%";
201 columns.size.aligned = "right";
203 columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency"));
204 columns.time.sortable = true;
205 columns.time.width = "10%";
206 columns.time.aligned = "right";
208 columns.timeline.title = "";
209 columns.timeline.sortable = false;
210 columns.timeline.width = "37%";
211 columns.timeline.sort = "ascending";
213 this._dataGrid = new WebInspector.DataGrid(columns);
214 this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
215 this.containerElement.appendChild(this._dataGrid.element);
216 this._dataGrid.addEventListener("sorting changed", this._sortItems, this);
217 this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this);
218 this._dataGrid.scrollContainer.addEventListener("scroll", this._updateOffscreenRows.bind(this));
220 this._patchTimelineHeader();
223 _makeHeaderFragment: function(title, subtitle)
225 var fragment = document.createDocumentFragment();
226 fragment.appendChild(document.createTextNode(title));
227 var subtitleDiv = document.createElement("div");
228 subtitleDiv.className = "network-header-subtitle";
229 subtitleDiv.textContent = subtitle;
230 fragment.appendChild(subtitleDiv);
234 _patchTimelineHeader: function()
236 var timelineSorting = document.createElement("select");
238 var option = document.createElement("option");
239 option.value = "startTime";
240 option.label = WebInspector.UIString("Timeline");
241 timelineSorting.appendChild(option);
243 option = document.createElement("option");
244 option.value = "startTime";
245 option.label = WebInspector.UIString("Start Time");
246 timelineSorting.appendChild(option);
248 option = document.createElement("option");
249 option.value = "responseTime";
250 option.label = WebInspector.UIString("Response Time");
251 timelineSorting.appendChild(option);
253 option = document.createElement("option");
254 option.value = "endTime";
255 option.label = WebInspector.UIString("End Time");
256 timelineSorting.appendChild(option);
258 option = document.createElement("option");
259 option.value = "duration";
260 option.label = WebInspector.UIString("Duration");
261 timelineSorting.appendChild(option);
263 option = document.createElement("option");
264 option.value = "latency";
265 option.label = WebInspector.UIString("Latency");
266 timelineSorting.appendChild(option);
268 var header = this._dataGrid.headerTableHeader("timeline");
269 header.replaceChild(timelineSorting, header.firstChild);
271 timelineSorting.addEventListener("click", function(event) { event.stopPropagation() }, false);
272 timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
273 this._timelineSortSelector = timelineSorting;
276 _createSortingFunctions: function()
278 this._sortingFunctions = {};
279 this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
280 this._sortingFunctions.method = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "method", false);
281 this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode", false);
282 this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType", false);
283 this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
284 this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", false);
285 this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
286 this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false);
287 this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "endTime", false);
288 this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "responseReceivedTime", false);
289 this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", true);
290 this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "latency", true);
292 var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
293 var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
295 this._calculators = {};
296 this._calculators.timeline = timeCalculator;
297 this._calculators.startTime = timeCalculator;
298 this._calculators.endTime = timeCalculator;
299 this._calculators.responseTime = timeCalculator;
300 this._calculators.duration = durationCalculator;
301 this._calculators.latency = durationCalculator;
304 _sortItems: function()
306 var columnIdentifier = this._dataGrid.sortColumnIdentifier;
307 if (columnIdentifier === "timeline") {
308 this._sortByTimeline();
311 var sortingFunction = this._sortingFunctions[columnIdentifier];
312 if (!sortingFunction)
315 this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending");
316 this._timelineSortSelector.selectedIndex = 0;
317 this._updateOffscreenRows();
320 _sortByTimeline: function()
322 var selectedIndex = this._timelineSortSelector.selectedIndex;
324 selectedIndex = 1; // Sort by start time by default.
325 var selectedOption = this._timelineSortSelector[selectedIndex];
326 var value = selectedOption.value;
328 var sortingFunction = this._sortingFunctions[value];
329 this._dataGrid.sortNodes(sortingFunction);
330 this.calculator = this._calculators[value];
331 if (this.calculator.startAtZero)
332 this._timelineGrid.hideEventDividers();
334 this._timelineGrid.showEventDividers();
335 this._dataGrid.markColumnAsSortedBy("timeline", "ascending");
336 this._updateOffscreenRows();
339 _createFilterStatusBarItems: function()
341 var filterBarElement = document.createElement("div");
342 filterBarElement.className = "scope-bar status-bar-item";
343 filterBarElement.id = "network-filter";
345 function createFilterElement(category, label)
347 var categoryElement = document.createElement("li");
348 categoryElement.category = category;
349 categoryElement.className = category;
350 categoryElement.appendChild(document.createTextNode(label));
351 categoryElement.addEventListener("click", this._updateFilter.bind(this), false);
352 filterBarElement.appendChild(categoryElement);
354 return categoryElement;
357 this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
360 var dividerElement = document.createElement("div");
361 dividerElement.addStyleClass("scope-bar-divider");
362 filterBarElement.appendChild(dividerElement);
364 for (var category in this._categories)
365 createFilterElement.call(this, category, this._categories[category].title);
366 this._filterBarElement = filterBarElement;
369 _createSummaryBar: function()
371 this._summaryBarElement = document.createElement("div");
372 this._summaryBarElement.className = "network-summary-bar";
373 this.containerElement.appendChild(this._summaryBarElement);
376 _updateSummaryBar: function()
378 this._positionSummaryBar(); // Grid is growing.
379 var numRequests = this._resources.length;
382 if (this._summaryBarElement._isDisplayingWarning)
384 this._summaryBarElement._isDisplayingWarning = true;
386 var img = document.createElement("img");
387 img.src = "Images/warningIcon.png";
388 this._summaryBarElement.removeChildren();
389 this._summaryBarElement.appendChild(img);
390 this._summaryBarElement.appendChild(document.createTextNode(" "));
391 this._summaryBarElement.appendChild(document.createTextNode(
392 WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
395 delete this._summaryBarElement._isDisplayingWarning;
397 var transferSize = 0;
400 for (var i = 0; i < this._resources.length; ++i) {
401 var resource = this._resources[i];
402 transferSize += (resource.cached || !resource.transferSize) ? 0 : resource.transferSize;
403 if (resource.isMainResource)
404 baseTime = resource.startTime;
405 if (resource.endTime > maxTime)
406 maxTime = resource.endTime;
408 var text = String.sprintf(WebInspector.UIString("%d requests"), numRequests);
409 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
410 if (baseTime !== -1 && this._mainResourceLoadTime !== -1 && this._mainResourceDOMContentTime !== -1 && this._mainResourceDOMContentTime > baseTime) {
411 text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
412 Number.secondsToString(maxTime - baseTime),
413 Number.secondsToString(this._mainResourceLoadTime - baseTime),
414 Number.secondsToString(this._mainResourceDOMContentTime - baseTime));
416 this._summaryBarElement.textContent = text;
419 _showCategory: function(category)
421 this._dataGrid.element.addStyleClass("filter-" + category);
422 delete this._hiddenCategories[category];
425 _hideCategory: function(category)
427 this._dataGrid.element.removeStyleClass("filter-" + category);
428 this._hiddenCategories[category] = true;
431 _updateFilter: function(e)
433 var isMac = WebInspector.isMac();
434 var selectMultiple = false;
435 if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
436 selectMultiple = true;
437 if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
438 selectMultiple = true;
440 this._filter(e.target, selectMultiple);
441 this._positionSummaryBar();
444 _filter: function(target, selectMultiple)
446 function unselectAll()
448 for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) {
449 var child = this._filterBarElement.childNodes[i];
453 child.removeStyleClass("selected");
454 this._hideCategory(child.category);
458 if (target.category === this._filterAllElement) {
459 if (target.hasStyleClass("selected")) {
460 // We can't unselect All, so we break early here
464 // If All wasn't selected, and now is, unselect everything else.
465 unselectAll.call(this);
467 // Something other than All is being selected, so we want to unselect All.
468 if (this._filterAllElement.hasStyleClass("selected")) {
469 this._filterAllElement.removeStyleClass("selected");
470 this._hideCategory("all");
474 if (!selectMultiple) {
475 // If multiple selection is off, we want to unselect everything else
476 // and just select ourselves.
477 unselectAll.call(this);
479 target.addStyleClass("selected");
480 this._showCategory(target.category);
484 if (target.hasStyleClass("selected")) {
485 // If selectMultiple is turned on, and we were selected, we just
486 // want to unselect ourselves.
487 target.removeStyleClass("selected");
488 this._hideCategory(target.category);
490 // If selectMultiple is turned on, and we weren't selected, we just
491 // want to select ourselves.
492 target.addStyleClass("selected");
493 this._showCategory(target.category);
495 this._updateOffscreenRows();
498 _scheduleRefresh: function()
500 if (this._needsRefresh)
503 this._needsRefresh = true;
505 if (this.visible && !("_refreshTimeout" in this))
506 this._refreshTimeout = setTimeout(this.refresh.bind(this), 500);
509 _updateDividersIfNeeded: function(force)
511 var timelineColumn = this._dataGrid.columns.timeline;
512 for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
513 if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnID) {
514 // Position timline grid location.
515 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
516 this._timelineGrid.element.style.right = "18px";
522 this._scheduleRefresh();
525 proceed = this._timelineGrid.updateDividers(force, this.calculator);
530 if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
531 // If our current sorting method starts at zero, that means it shows all
532 // resources starting at the same point, and so onLoad event and DOMContent
533 // event lines really wouldn't make much sense here, so don't render them.
534 // Additionally, if the calculator doesn't have the computePercentageFromEventTime
535 // function defined, we are probably sorting by size, and event times aren't relevant
540 this._timelineGrid.removeEventDividers();
541 if (this._mainResourceLoadTime !== -1) {
542 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceLoadTime);
544 var loadDivider = document.createElement("div");
545 loadDivider.className = "network-event-divider network-red-divider";
547 var loadDividerPadding = document.createElement("div");
548 loadDividerPadding.className = "network-event-divider-padding";
549 loadDividerPadding.title = WebInspector.UIString("Load event fired");
550 loadDividerPadding.appendChild(loadDivider);
551 loadDividerPadding.style.left = percent + "%";
552 this._timelineGrid.addEventDivider(loadDividerPadding);
555 if (this._mainResourceDOMContentTime !== -1) {
556 var percent = this.calculator.computePercentageFromEventTime(this._mainResourceDOMContentTime);
558 var domContentDivider = document.createElement("div");
559 domContentDivider.className = "network-event-divider network-blue-divider";
561 var domContentDividerPadding = document.createElement("div");
562 domContentDividerPadding.className = "network-event-divider-padding";
563 domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
564 domContentDividerPadding.appendChild(domContentDivider);
565 domContentDividerPadding.style.left = percent + "%";
566 this._timelineGrid.addEventDivider(domContentDividerPadding);
570 _refreshIfNeeded: function()
572 if (this._needsRefresh)
576 _invalidateAllItems: function()
578 this._staleResources = this._resources.slice();
583 return this._calculator;
588 if (!x || this._calculator === x)
591 this._calculator = x;
592 this._calculator.reset();
594 this._invalidateAllItems();
598 _resourceGridNode: function(resource)
600 return this._resourceGridNodes[resource.identifier];
603 revealAndSelectItem: function(resource)
605 var node = this._resourceGridNode(resource);
612 addEventDivider: function(divider)
614 this._timelineGrid.addEventDivider(divider);
617 _createStatusbarButtons: function()
619 this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
620 this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked.bind(this), false);
622 this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
623 this._clearButton.addEventListener("click", this._reset.bind(this), false);
625 this._largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
626 this._largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows;
627 this._largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
630 set mainResourceLoadTime(x)
632 if (this._mainResourceLoadTime === x)
635 this._mainResourceLoadTime = x || -1;
636 // Update the dividers to draw the new line
637 this._updateDividersIfNeeded(true);
640 set mainResourceDOMContentTime(x)
642 if (this._mainResourceDOMContentTime === x)
645 this._mainResourceDOMContentTime = x || -1;
646 this._updateDividersIfNeeded(true);
651 WebInspector.Panel.prototype.show.call(this);
652 this._refreshIfNeeded();
654 if (this.visibleView)
655 this.visibleView.show(this._viewsContainerElement);
657 this._dataGrid.updateWidths();
658 this._positionSummaryBar();
663 WebInspector.Panel.prototype.hide.call(this);
664 this._popoverHelper.hidePopup();
667 get searchableViews()
673 searchMatchFound: function(view, matches)
675 this._resourceGridNode(view.resource).searchMatches = matches;
678 searchCanceled: function(startingNewSearch)
680 WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
682 if (startingNewSearch || !this._resources)
686 performSearch: function(query)
688 WebInspector.Panel.prototype.performSearch.call(this, query);
693 this._needsRefresh = false;
694 if ("_refreshTimeout" in this) {
695 clearTimeout(this._refreshTimeout);
696 delete this._refreshTimeout;
699 var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
700 var staleItemsLength = this._staleResources.length;
701 var boundariesChanged = false;
703 for (var i = 0; i < staleItemsLength; ++i) {
704 var resource = this._staleResources[i];
705 var node = this._resourceGridNode(resource);
707 // Create the timeline tree element and graph.
708 node = new WebInspector.NetworkDataGridNode(this, resource);
709 this._resourceGridNodes[resource.identifier] = node;
710 this._dataGrid.appendChild(node);
712 node.refreshResource();
714 if (this.calculator.updateBoundaries(resource))
715 boundariesChanged = true;
718 if (boundariesChanged) {
719 // The boundaries changed, so all item graphs are stale.
720 this._invalidateAllItems();
721 staleItemsLength = this._staleResources.length;
724 for (var i = 0; i < staleItemsLength; ++i)
725 this._resourceGridNode(this._staleResources[i]).refreshGraph(this.calculator);
727 this._staleResources = [];
729 this._updateSummaryBar();
730 this._dataGrid.updateWidths();
732 if (wasScrolledToLastRow)
733 this._dataGrid.scrollToLastRow();
736 _onPreserveLogClicked: function(e)
738 this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
743 this._popoverHelper.hidePopup();
744 this._closeVisibleResource();
746 this._toggleGridMode();
748 // Begin reset timeline
749 if (this._calculator)
750 this._calculator.reset();
752 this._resources = [];
753 this._resourcesById = {};
754 this._resourcesByURL = {};
755 this._staleResources = [];
756 this._resourceGridNodes = {};
758 this._dataGrid.removeChildren();
759 delete this._summaryBarRowNode;
760 this._updateDividersIfNeeded(true);
761 // End reset timeline.
763 this._mainResourceLoadTime = -1;
764 this._mainResourceDOMContentTime = -1;
766 this._viewsContainerElement.removeChildren();
767 this._viewsContainerElement.appendChild(this._closeButtonElement);
768 this._resetSummaryBar();
773 return this._resources;
776 resourceById: function(id)
778 return this._resourcesById[id];
781 _onResourceStarted: function(event)
783 this._appendResource(event.data);
786 _appendResource: function(resource)
788 this._resources.push(resource);
789 this._resourcesById[resource.identifier] = resource;
790 this._resourcesByURL[resource.url] = resource;
792 // Pull all the redirects of the main resource upon commit load.
793 if (resource.redirects) {
794 for (var i = 0; i < resource.redirects.length; ++i)
795 this._refreshResource(resource.redirects[i]);
798 this._refreshResource(resource);
801 _onResourceUpdated: function(event)
803 this._refreshResource(event.data);
806 _refreshResource: function(resource)
808 this._staleResources.push(resource);
809 this._scheduleRefresh();
811 var oldView = WebInspector.ResourceView.existingResourceViewForResource(resource);
815 if (WebInspector.ResourceView.resourceViewTypeMatchesResource(resource))
818 var newView = WebInspector.ResourceView.recreateResourceView(resource);
819 if (this.visibleView === oldView)
820 this.visibleView = newView;
825 if (this._preserveLogToggle.toggled)
830 _onMainResourceCommitLoad: function()
832 if (this._preserveLogToggle.toggled)
836 // Now resurrect the main resource along with all redirects that lead to it.
837 var resourcesToAppend = (WebInspector.mainResource.redirects || []).concat(WebInspector.mainResource);
838 resourcesToAppend.forEach(this._appendResource, this);
841 canShowSourceLine: function(url, line)
843 return !!this._resourcesByURL[url];
846 showSourceLine: function(url, line)
848 this._showResource(this._resourcesByURL[url], line);
851 _showResource: function(resource, line)
856 this._popoverHelper.hidePopup();
858 this._toggleViewingResourceMode();
860 if (this.visibleView) {
861 this.visibleView.detach();
862 delete this.visibleView;
865 var view = new WebInspector.NetworkItemView(resource);
866 view.show(this._viewsContainerElement);
867 this.visibleView = view;
869 this.updateSidebarWidth();
872 _closeVisibleResource: function()
874 this.element.removeStyleClass("viewing-resource");
876 if (this.visibleView) {
877 this.visibleView.detach();
878 delete this.visibleView;
881 if (this._lastSelectedGraphTreeElement)
882 this._lastSelectedGraphTreeElement.select(true);
884 this.updateSidebarWidth();
887 _toggleLargerResources: function()
889 WebInspector.settings.resourcesLargeRows = !WebInspector.settings.resourcesLargeRows;
890 this._setLargerResources(WebInspector.settings.resourcesLargeRows);
893 _setLargerResources: function(enabled)
895 this._largerResourcesButton.toggled = enabled;
897 this._largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
898 this._dataGrid.element.addStyleClass("small");
899 this._timelineGrid.element.addStyleClass("small");
900 this._viewsContainerElement.addStyleClass("small");
902 this._largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
903 this._dataGrid.element.removeStyleClass("small");
904 this._timelineGrid.element.removeStyleClass("small");
905 this._viewsContainerElement.removeStyleClass("small");
907 this._positionSummaryBar();
910 _getPopoverAnchor: function(element)
912 var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
915 var resource = anchor.parentElement.resource;
916 return resource && resource.timing ? anchor : null;
919 _showPopover: function(anchor)
921 var resource = anchor.parentElement.resource;
922 var tableElement = WebInspector.ResourceTimingView.createTimingTable(resource);
923 var popover = new WebInspector.Popover(tableElement);
924 popover.show(anchor);
928 _toggleGridMode: function()
930 if (this._viewingResourceMode) {
931 this._viewingResourceMode = false;
932 this.element.removeStyleClass("viewing-resource");
933 this._dataGrid.element.removeStyleClass("viewing-resource-mode");
934 this._viewsContainerElement.addStyleClass("hidden");
935 this.sidebarElement.style.right = 0;
936 this.sidebarElement.style.removeProperty("width");
937 if (this._dataGrid.selectedNode)
938 this._dataGrid.selectedNode.selected = false;
941 if (this._briefGrid) {
942 this._dataGrid.element.removeStyleClass("full-grid-mode");
943 this._dataGrid.element.addStyleClass("brief-grid-mode");
945 this._dataGrid.hideColumn("method");
946 this._dataGrid.hideColumn("status");
947 this._dataGrid.hideColumn("type");
948 this._dataGrid.hideColumn("size");
949 this._dataGrid.hideColumn("time");
953 widths.timeline = 80;
955 this._dataGrid.element.addStyleClass("full-grid-mode");
956 this._dataGrid.element.removeStyleClass("brief-grid-mode");
958 this._dataGrid.showColumn("method");
959 this._dataGrid.showColumn("status");
960 this._dataGrid.showColumn("type");
961 this._dataGrid.showColumn("size");
962 this._dataGrid.showColumn("time");
971 widths.timeline = 37;
974 this._dataGrid.showColumn("timeline");
975 this._dataGrid.applyColumnWidthsMap(widths);
979 _toggleViewingResourceMode: function()
981 if (this._viewingResourceMode)
983 this._viewingResourceMode = true;
984 this._preservedColumnWidths = this._dataGrid.columnWidthsMap();
986 this.element.addStyleClass("viewing-resource");
987 this._dataGrid.element.addStyleClass("viewing-resource-mode");
988 this._dataGrid.element.removeStyleClass("full-grid-mode");
989 this._dataGrid.element.removeStyleClass("brief-grid-mode");
991 this._dataGrid.hideColumn("method");
992 this._dataGrid.hideColumn("status");
993 this._dataGrid.hideColumn("type");
994 this._dataGrid.hideColumn("size");
995 this._dataGrid.hideColumn("time");
996 this._dataGrid.hideColumn("timeline");
998 this._viewsContainerElement.removeStyleClass("hidden");
999 this.updateSidebarWidth(200);
1003 this._dataGrid.applyColumnWidthsMap(widths);
1006 _contextMenu: function(event)
1008 // createBlobURL is enabled conditionally, do not expose resource export if it's not available.
1009 if (typeof window.webkitURL.createObjectURL !== "function" || !Preferences.resourceExportEnabled)
1012 var contextMenu = new WebInspector.ContextMenu();
1013 var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1014 var resource = gridNode && gridNode._resource;
1016 contextMenu.appendItem(WebInspector.UIString("Export to HAR"), this._exportResource.bind(this, resource));
1017 contextMenu.appendItem(WebInspector.UIString("Export all to HAR"), this._exportAll.bind(this));
1018 contextMenu.show(event);
1021 _exportAll: function()
1024 log: (new WebInspector.HARLog()).build()
1026 InspectorFrontendHost.copyText(JSON.stringify(harArchive));
1029 _exportResource: function(resource)
1031 var har = (new WebInspector.HAREntry(resource)).build();
1032 InspectorFrontendHost.copyText(JSON.stringify(har));
1035 _updateOffscreenRows: function(e)
1037 var dataTableBody = this._dataGrid.dataTableBody;
1038 var rows = dataTableBody.children;
1039 var recordsCount = rows.length;
1040 if (recordsCount < 2)
1041 return; // Filler row only.
1043 var visibleTop = this._dataGrid.scrollContainer.scrollTop;
1044 var visibleBottom = visibleTop + this._dataGrid.scrollContainer.offsetHeight;
1048 // Filler is at recordsCount - 1.
1049 var unfilteredRowIndex = 0;
1050 for (var i = 0; i < recordsCount - 1; ++i) {
1052 // Don't touch summaty - quit instead.
1053 if (this._summaryBarRowNode && row === this._summaryBarRowNode.element)
1056 var dataGridNode = this._dataGrid.dataGridNodeFromNode(row);
1057 if (dataGridNode.isFilteredOut()) {
1058 row.removeStyleClass("offscreen");
1063 rowHeight = row.offsetHeight;
1065 var rowIsVisible = unfilteredRowIndex * rowHeight < visibleBottom && (unfilteredRowIndex + 1) * rowHeight > visibleTop;
1066 if (rowIsVisible !== row.rowIsVisible) {
1068 row.removeStyleClass("offscreen");
1070 row.addStyleClass("offscreen");
1071 row.rowIsVisible = rowIsVisible;
1073 unfilteredRowIndex++;
1078 WebInspector.NetworkPanel.prototype.__proto__ = WebInspector.Panel.prototype;
1080 WebInspector.NetworkBaseCalculator = function()
1084 WebInspector.NetworkBaseCalculator.prototype = {
1085 computeSummaryValues: function(items)
1088 var categoryValues = {};
1090 var itemsLength = items.length;
1091 for (var i = 0; i < itemsLength; ++i) {
1092 var item = items[i];
1093 var value = this._value(item);
1094 if (typeof value === "undefined")
1096 if (!(item.category.name in categoryValues))
1097 categoryValues[item.category.name] = 0;
1098 categoryValues[item.category.name] += value;
1102 return {categoryValues: categoryValues, total: total};
1105 computeBarGraphPercentages: function(item)
1107 return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan) * 100};
1110 computeBarGraphLabels: function(item)
1112 const label = this.formatValue(this._value(item));
1113 return {left: label, right: label, tooltip: label};
1118 return this.maximumBoundary - this.minimumBoundary;
1121 updateBoundaries: function(item)
1123 this.minimumBoundary = 0;
1125 var value = this._value(item);
1126 if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) {
1127 this.maximumBoundary = value;
1135 delete this.minimumBoundary;
1136 delete this.maximumBoundary;
1139 _value: function(item)
1144 formatValue: function(value)
1146 return value.toString();
1150 WebInspector.NetworkTimeCalculator = function(startAtZero)
1152 WebInspector.NetworkBaseCalculator.call(this);
1153 this.startAtZero = startAtZero;
1156 WebInspector.NetworkTimeCalculator.prototype = {
1157 computeSummaryValues: function(resources)
1159 var resourcesByCategory = {};
1160 var resourcesLength = resources.length;
1161 for (var i = 0; i < resourcesLength; ++i) {
1162 var resource = resources[i];
1163 if (!(resource.category.name in resourcesByCategory))
1164 resourcesByCategory[resource.category.name] = [];
1165 resourcesByCategory[resource.category.name].push(resource);
1170 var categoryValues = {};
1171 for (var category in resourcesByCategory) {
1172 resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
1173 categoryValues[category] = 0;
1175 var segment = {start: -1, end: -1};
1177 var categoryResources = resourcesByCategory[category];
1178 var resourcesLength = categoryResources.length;
1179 for (var i = 0; i < resourcesLength; ++i) {
1180 var resource = categoryResources[i];
1181 if (resource.startTime === -1 || resource.endTime === -1)
1184 if (typeof earliestStart === "undefined")
1185 earliestStart = resource.startTime;
1187 earliestStart = Math.min(earliestStart, resource.startTime);
1189 if (typeof latestEnd === "undefined")
1190 latestEnd = resource.endTime;
1192 latestEnd = Math.max(latestEnd, resource.endTime);
1194 if (resource.startTime <= segment.end) {
1195 segment.end = Math.max(segment.end, resource.endTime);
1199 categoryValues[category] += segment.end - segment.start;
1201 segment.start = resource.startTime;
1202 segment.end = resource.endTime;
1205 // Add the last segment
1206 categoryValues[category] += segment.end - segment.start;
1209 return {categoryValues: categoryValues, total: latestEnd - earliestStart};
1212 computeBarGraphPercentages: function(resource)
1214 if (resource.startTime !== -1)
1215 var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
1219 if (resource.responseReceivedTime !== -1)
1220 var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
1222 var middle = (this.startAtZero ? start : 100);
1224 if (resource.endTime !== -1)
1225 var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
1227 var end = (this.startAtZero ? middle : 100);
1229 if (this.startAtZero) {
1235 return {start: start, middle: middle, end: end};
1238 computePercentageFromEventTime: function(eventTime)
1240 // This function computes a percentage in terms of the total loading time
1241 // of a specific event. If startAtZero is set, then this is useless, and we
1242 // want to return 0.
1243 if (eventTime !== -1 && !this.startAtZero)
1244 return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100;
1249 computeBarGraphLabels: function(resource)
1251 var rightLabel = "";
1252 if (resource.responseReceivedTime !== -1 && resource.endTime !== -1)
1253 rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime);
1255 var hasLatency = resource.latency > 0;
1257 var leftLabel = this.formatValue(resource.latency);
1259 var leftLabel = rightLabel;
1261 if (resource.timing)
1262 return {left: leftLabel, right: rightLabel};
1264 if (hasLatency && rightLabel) {
1265 var total = this.formatValue(resource.duration);
1266 var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
1267 } else if (hasLatency)
1268 var tooltip = WebInspector.UIString("%s latency", leftLabel);
1269 else if (rightLabel)
1270 var tooltip = WebInspector.UIString("%s download", rightLabel);
1272 if (resource.cached)
1273 tooltip = WebInspector.UIString("%s (from cache)", tooltip);
1274 return {left: leftLabel, right: rightLabel, tooltip: tooltip};
1277 updateBoundaries: function(resource)
1279 var didChange = false;
1282 if (this.startAtZero)
1285 lowerBound = this._lowerBound(resource);
1287 if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
1288 this.minimumBoundary = lowerBound;
1292 var upperBound = this._upperBound(resource);
1293 if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
1294 this.maximumBoundary = upperBound;
1301 formatValue: function(value)
1303 return Number.secondsToString(value);
1306 _lowerBound: function(resource)
1311 _upperBound: function(resource)
1317 WebInspector.NetworkTimeCalculator.prototype.__proto__ = WebInspector.NetworkBaseCalculator.prototype;
1319 WebInspector.NetworkTransferTimeCalculator = function()
1321 WebInspector.NetworkTimeCalculator.call(this, false);
1324 WebInspector.NetworkTransferTimeCalculator.prototype = {
1325 formatValue: function(value)
1327 return Number.secondsToString(value);
1330 _lowerBound: function(resource)
1332 return resource.startTime;
1335 _upperBound: function(resource)
1337 return resource.endTime;
1341 WebInspector.NetworkTransferTimeCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1343 WebInspector.NetworkTransferDurationCalculator = function()
1345 WebInspector.NetworkTimeCalculator.call(this, true);
1348 WebInspector.NetworkTransferDurationCalculator.prototype = {
1349 formatValue: function(value)
1351 return Number.secondsToString(value);
1354 _upperBound: function(resource)
1356 return resource.duration;
1360 WebInspector.NetworkTransferDurationCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype;
1362 WebInspector.NetworkDataGridNode = function(panel, resource)
1364 WebInspector.DataGridNode.call(this, {});
1365 this._panel = panel;
1366 this._resource = resource;
1369 WebInspector.NetworkDataGridNode.prototype = {
1370 createCells: function()
1372 this._nameCell = this._createDivInTD("name");
1373 this._methodCell = this._createDivInTD("method");
1374 this._statusCell = this._createDivInTD("status");
1375 this._typeCell = this._createDivInTD("type");
1376 this._sizeCell = this._createDivInTD("size");
1377 this._timeCell = this._createDivInTD("time");
1378 this._createTimelineCell();
1379 this._nameCell.addEventListener("click", this.select.bind(this), false);
1380 this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
1383 isFilteredOut: function()
1385 if (!this._panel._hiddenCategories.all)
1387 return this._resource.category.name in this._panel._hiddenCategories;
1392 this._panel._showResource(this._resource);
1393 WebInspector.DataGridNode.prototype.select.apply(this, arguments);
1396 _openInNewTab: function()
1398 InspectorBackend.openInInspectedWindow(this._resource.url);
1403 if (!this._panel._viewingResourceMode)
1405 return !this.isFilteredOut();
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";
1423 this._barAreaElement = document.createElement("div");
1424 // this._barAreaElement.className = "network-graph-bar-area hidden";
1425 this._barAreaElement.className = "network-graph-bar-area";
1426 this._barAreaElement.resource = this._resource;
1427 this._graphElement.appendChild(this._barAreaElement);
1429 this._barLeftElement = document.createElement("div");
1430 this._barLeftElement.className = "network-graph-bar waiting";
1431 this._barAreaElement.appendChild(this._barLeftElement);
1433 this._barRightElement = document.createElement("div");
1434 this._barRightElement.className = "network-graph-bar";
1435 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._graphElement.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
1448 this._timelineCell = document.createElement("td");
1449 this._timelineCell.className = "timeline-column";
1450 this._element.appendChild(this._timelineCell);
1451 this._timelineCell.appendChild(this._graphElement);
1454 refreshResource: function()
1456 this._refreshNameCell();
1458 this._methodCell.textContent = this._resource.requestMethod;
1460 this._refreshStatusCell();
1462 if (this._resource.mimeType) {
1463 this._typeCell.removeStyleClass("network-dim-cell");
1464 this._typeCell.textContent = this._resource.mimeType;
1466 this._typeCell.addStyleClass("network-dim-cell");
1467 this._typeCell.textContent = WebInspector.UIString("Pending");
1470 this._refreshSizeCell();
1471 this._refreshTimeCell();
1473 if (this._resource.cached)
1474 this._graphElement.addStyleClass("resource-cached");
1476 this._element.addStyleClass("network-item");
1477 if (!this._element.hasStyleClass("network-category-" + this._resource.category.name)) {
1478 this._element.removeMatchingStyleClasses("network-category-\\w+");
1479 this._element.addStyleClass("network-category-" + this._resource.category.name);
1483 _refreshNameCell: function()
1485 this._nameCell.removeChildren();
1487 if (this._resource.category === WebInspector.resourceCategories.images) {
1488 var previewImage = document.createElement("img");
1489 previewImage.className = "image-network-icon-preview";
1490 this._resource.populateImageSource(previewImage);
1492 var iconElement = document.createElement("div");
1493 iconElement.className = "icon";
1494 iconElement.appendChild(previewImage);
1496 var iconElement = document.createElement("img");
1497 iconElement.className = "icon";
1499 this._nameCell.appendChild(iconElement);
1500 this._nameCell.appendChild(document.createTextNode(this._fileName()));
1503 var subtitle = this._resource.displayDomain;
1505 if (this._resource.path && this._resource.lastPathComponent) {
1506 var lastPathComponentIndex = this._resource.path.lastIndexOf("/" + this._resource.lastPathComponent);
1507 if (lastPathComponentIndex != -1)
1508 subtitle += this._resource.path.substring(0, lastPathComponentIndex);
1511 this._appendSubtitle(this._nameCell, subtitle);
1512 this._nameCell.title = this._resource.url;
1515 _fileName: function()
1517 var fileName = this._resource.displayName;
1518 if (this._resource.queryString)
1519 fileName += "?" + this._resource.queryString;
1523 _refreshStatusCell: function()
1525 this._statusCell.removeChildren();
1527 var fromCache = this._resource.cached;
1529 this._statusCell.textContent = WebInspector.UIString("(from cache)");
1530 this._statusCell.addStyleClass("network-dim-cell");
1534 this._statusCell.removeStyleClass("network-dim-cell");
1535 if (this._resource.statusCode) {
1536 this._statusCell.appendChild(document.createTextNode(this._resource.statusCode));
1537 this._statusCell.removeStyleClass("network-dim-cell");
1538 this._appendSubtitle(this._statusCell, this._resource.statusText);
1539 this._statusCell.title = this._resource.statusCode + " " + this._resource.statusText;
1541 this._statusCell.addStyleClass("network-dim-cell");
1542 this._statusCell.textContent = WebInspector.UIString("Pending");
1546 _refreshSizeCell: function()
1548 var resourceSize = typeof this._resource.resourceSize === "number" ? Number.bytesToString(this._resource.resourceSize) : "?";
1549 var transferSize = typeof this._resource.transferSize === "number" ? Number.bytesToString(this._resource.transferSize) : "?";
1550 var fromCache = this._resource.cached;
1551 this._sizeCell.textContent = !fromCache ? resourceSize : WebInspector.UIString("(from cache)");
1553 this._sizeCell.addStyleClass("network-dim-cell");
1555 this._sizeCell.removeStyleClass("network-dim-cell");
1557 this._appendSubtitle(this._sizeCell, transferSize);
1560 _refreshTimeCell: function()
1562 if (this._resource.duration > 0) {
1563 this._timeCell.removeStyleClass("network-dim-cell");
1564 this._timeCell.textContent = Number.secondsToString(this._resource.duration);
1565 this._appendSubtitle(this._timeCell, Number.secondsToString(this._resource.latency));
1567 this._timeCell.addStyleClass("network-dim-cell");
1568 this._timeCell.textContent = WebInspector.UIString("Pending");
1572 _appendSubtitle: function(cellElement, subtitleText)
1574 var subtitleElement = document.createElement("div");
1575 subtitleElement.className = "network-cell-subtitle";
1576 subtitleElement.textContent = subtitleText;
1577 cellElement.appendChild(subtitleElement);
1580 refreshGraph: function(calculator)
1582 var percentages = calculator.computeBarGraphPercentages(this._resource);
1583 this._percentages = percentages;
1585 this._barAreaElement.removeStyleClass("hidden");
1587 if (!this._graphElement.hasStyleClass("network-category-" + this._resource.category.name)) {
1588 this._graphElement.removeMatchingStyleClasses("network-category-\\w+");
1589 this._graphElement.addStyleClass("network-category-" + this._resource.category.name);
1592 this._barLeftElement.style.setProperty("left", percentages.start + "%");
1593 this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
1595 this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
1596 this._barRightElement.style.setProperty("left", percentages.middle + "%");
1598 var labels = calculator.computeBarGraphLabels(this._resource);
1599 this._labelLeftElement.textContent = labels.left;
1600 this._labelRightElement.textContent = labels.right;
1602 var tooltip = (labels.tooltip || "");
1603 this._barLeftElement.title = tooltip;
1604 this._labelLeftElement.title = tooltip;
1605 this._labelRightElement.title = tooltip;
1606 this._barRightElement.title = tooltip;
1609 _refreshLabelPositions: function()
1611 if (!this._percentages)
1613 this._labelLeftElement.style.removeProperty("left");
1614 this._labelLeftElement.style.removeProperty("right");
1615 this._labelLeftElement.removeStyleClass("before");
1616 this._labelLeftElement.removeStyleClass("hidden");
1618 this._labelRightElement.style.removeProperty("left");
1619 this._labelRightElement.style.removeProperty("right");
1620 this._labelRightElement.removeStyleClass("after");
1621 this._labelRightElement.removeStyleClass("hidden");
1623 const labelPadding = 10;
1624 const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
1625 const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
1627 if (this._barLeftElement) {
1628 var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
1629 var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
1631 var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
1632 var rightBarWidth = barRightElementOffsetWidth - labelPadding;
1635 const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
1636 const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
1638 const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
1639 const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
1640 const graphElementOffsetWidth = this._graphElement.offsetWidth;
1642 if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
1643 var leftHidden = true;
1645 if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
1646 var rightHidden = true;
1648 if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
1649 // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
1650 if (labelBefore && !labelAfter)
1652 else if (labelAfter && !labelBefore)
1658 this._labelLeftElement.addStyleClass("hidden");
1659 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
1660 this._labelLeftElement.addStyleClass("before");
1662 this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
1663 this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
1668 this._labelRightElement.addStyleClass("hidden");
1669 this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
1670 this._labelRightElement.addStyleClass("after");
1672 this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
1673 this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
1678 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
1680 var aFileName = a._resource.displayName + (a._resource.queryString ? a._resource.queryString : "");
1681 var bFileName = b._resource.displayName + (b._resource.queryString ? b._resource.queryString : "");
1682 if (aFileName > bFileName)
1684 if (bFileName > aFileName)
1689 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
1691 if (b._resource.cached && !a._resource.cached)
1693 if (a._resource.cached && !b._resource.cached)
1696 if (a._resource.resourceSize === b._resource.resourceSize)
1699 return a._resource.resourceSize - b._resource.resourceSize;
1702 WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyName, revert, a, b)
1704 var aValue = a._resource[propertyName];
1705 var bValue = b._resource[propertyName];
1706 if (aValue > bValue)
1707 return revert ? -1 : 1;
1708 if (bValue > aValue)
1709 return revert ? 1 : -1;
1713 WebInspector.NetworkDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
1715 WebInspector.NetworkTotalGridNode = function(element)
1717 this._summaryBarElement = element;
1718 WebInspector.DataGridNode.call(this, {summaryRow: true});
1721 WebInspector.NetworkTotalGridNode.prototype = {
1722 isFilteredOut: function()
1732 createCells: function()
1734 var td = document.createElement("td");
1735 td.setAttribute("colspan", 7);
1736 td.className = "network-summary";
1737 td.appendChild(this._summaryBarElement);
1738 this._element.appendChild(td);
1742 WebInspector.NetworkTotalGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;