Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / network / NetworkPanel.js
1 /*
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.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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.
18  *
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.
29  */
30
31 importScript("RequestView.js");
32 importScript("NetworkItemView.js");
33 importScript("RequestCookiesView.js");
34 importScript("RequestHeadersView.js");
35 importScript("RequestHTMLView.js");
36 importScript("RequestJSONView.js");
37 importScript("RequestPreviewView.js");
38 importScript("RequestResponseView.js");
39 importScript("RequestTimingView.js");
40 importScript("ResourceWebSocketFrameView.js");
41
42 /**
43  * @constructor
44  * @implements {WebInspector.Searchable}
45  * @implements {WebInspector.TargetManager.Observer}
46  * @extends {WebInspector.VBox}
47  * @param {!WebInspector.FilterBar} filterBar
48  * @param {!WebInspector.Setting} coulmnsVisibilitySetting
49  */
50 WebInspector.NetworkLogView = function(filterBar, coulmnsVisibilitySetting)
51 {
52     WebInspector.VBox.call(this);
53     this.registerRequiredCSS("networkLogView.css");
54     this.registerRequiredCSS("filter.css");
55     this.registerRequiredCSS("suggestBox.css");
56
57     this._filterBar = filterBar;
58     this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
59     this._allowRequestSelection = false;
60     this._requests = [];
61     this._requestsById = {};
62     this._requestsByURL = {};
63     this._staleRequests = {};
64     this._requestGridNodes = {};
65     this._lastRequestGridNodeId = 0;
66     this._mainRequestLoadTime = -1;
67     this._mainRequestDOMContentLoadedTime = -1;
68     this._matchedRequests = [];
69     this._highlightedSubstringChanges = [];
70     this._filteredOutRequests = new Map();
71
72     /** @type {!Array.<!WebInspector.NetworkLogView.Filter>} */
73     this._filters = [];
74
75     this._matchedRequestsMap = {};
76     this._currentMatchedRequestIndex = -1;
77
78     this._createStatusbarButtons();
79     this._createStatusBarItems();
80     this._linkifier = new WebInspector.Linkifier();
81
82     this._addFilters();
83     this._resetSuggestionBuilder();
84     this._initializeView();
85     this._recordButton.toggled = true;
86
87     WebInspector.targetManager.observeTargets(this);
88 }
89
90 WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
91 WebInspector.NetworkLogView._responseHeaderColumns = ["Cache-Control", "Connection", "Content-Encoding", "Content-Length", "ETag", "Keep-Alive", "Last-Modified", "Server", "Vary"];
92 WebInspector.NetworkLogView._defaultColumnsVisibility = {
93     method: true, status: true, scheme: false, domain: false, remoteAddress: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true,
94     "Cache-Control": false, "Connection": false, "Content-Encoding": false, "Content-Length": false, "ETag": false, "Keep-Alive": false, "Last-Modified": false, "Server": false, "Vary": false
95 };
96 WebInspector.NetworkLogView._defaultRefreshDelay = 500;
97
98 WebInspector.NetworkLogView.prototype = {
99     /**
100      * @param {!WebInspector.Target} target
101      */
102     targetAdded: function(target)
103     {
104         target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
105         target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
106         target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
107
108         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
109         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
110         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
111         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
112
113         target.networkLog.requests.forEach(this._appendRequest.bind(this));
114     },
115
116     /**
117      * @param {!WebInspector.Target} target
118      */
119     targetRemoved: function(target)
120     {
121         target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
122         target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
123         target.networkManager.removeEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
124
125         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
126         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
127         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
128         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
129     },
130
131     _addFilters: function()
132     {
133         this._textFilterUI = new WebInspector.TextFilterUI();
134         this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
135         this._filterBar.addFilter(this._textFilterUI);
136
137         var types = [];
138         for (var typeId in WebInspector.resourceTypes) {
139             var resourceType = WebInspector.resourceTypes[typeId];
140             types.push({name: resourceType.name(), label: resourceType.categoryTitle()});
141         }
142         this._resourceTypeFilterUI = new WebInspector.NamedBitSetFilterUI(types, WebInspector.settings.networkResourceTypeFilters);
143         this._resourceTypeFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
144         this._filterBar.addFilter(this._resourceTypeFilterUI);
145
146         var dataURLSetting = WebInspector.settings.networkHideDataURL;
147         this._dataURLFilterUI = new WebInspector.CheckboxFilterUI("hide-data-url", WebInspector.UIString("Hide data URLs"), true, dataURLSetting);
148         this._dataURLFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged.bind(this), this);
149         this._filterBar.addFilter(this._dataURLFilterUI);
150     },
151
152     _resetSuggestionBuilder: function()
153     {
154         this._suggestionBuilder = new WebInspector.FilterSuggestionBuilder(WebInspector.NetworkPanel._searchKeys);
155         this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
156     },
157
158     _filterChanged: function(event)
159     {
160         this._removeAllNodeHighlights();
161         this.searchCanceled();
162         this._parseFilterQuery(this._textFilterUI.value());
163         this._filterRequests();
164     },
165
166     _initializeView: function()
167     {
168         this.element.id = "network-container";
169
170         this._createSortingFunctions();
171         this._createTable();
172         this._createTimelineGrid();
173         this._summaryBarElement = this.element.createChild("div", "network-summary-bar");
174
175         if (!this.useLargeRows)
176             this._setLargerRequests(this.useLargeRows);
177
178         this._allowPopover = true;
179         this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
180         // Enable faster hint.
181         this._popoverHelper.setTimeout(100);
182
183         this.calculator = new WebInspector.NetworkTransferTimeCalculator();
184
185         this.switchToDetailedView();
186     },
187
188     get statusBarItems()
189     {
190         return [
191             this._recordButton.element,
192             this._clearButton.element,
193             this._filterBar.filterButton().element,
194             this._largerRequestsButton.element,
195             this._preserveLogCheckbox.element,
196             this._disableCacheCheckbox.element,
197             this._progressBarContainer];
198     },
199
200     get useLargeRows()
201     {
202         return WebInspector.settings.resourcesLargeRows.get();
203     },
204
205     set allowPopover(flag)
206     {
207         this._allowPopover = flag;
208     },
209
210     /**
211      * @return {!Array.<!Element>}
212      */
213     elementsToRestoreScrollPositionsFor: function()
214     {
215         if (!this._dataGrid) // Not initialized yet.
216             return [];
217         return [this._dataGrid.scrollContainer];
218     },
219
220     _createTimelineGrid: function()
221     {
222         this._timelineGrid = new WebInspector.TimelineGrid();
223         this._timelineGrid.element.classList.add("network-timeline-grid");
224         this._dataGrid.element.appendChild(this._timelineGrid.element);
225     },
226
227     _createTable: function()
228     {
229         var columns = [];
230         columns.push({
231             id: "name",
232             titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
233             title: WebInspector.UIString("Name"),
234             sortable: true,
235             weight: 20,
236             disclosure: true
237         });
238
239         columns.push({
240             id: "method",
241             title: WebInspector.UIString("Method"),
242             sortable: true,
243             weight: 6
244         });
245
246         columns.push({
247             id: "status",
248             titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
249             title: WebInspector.UIString("Status"),
250             sortable: true,
251             weight: 6
252         });
253
254         columns.push({
255             id: "scheme",
256             title: WebInspector.UIString("Scheme"),
257             sortable: true,
258             weight: 6
259         });
260
261         columns.push({
262             id: "domain",
263             title: WebInspector.UIString("Domain"),
264             sortable: true,
265             weight: 6
266         });
267
268         columns.push({
269             id: "remoteAddress",
270             title: WebInspector.UIString("Remote Address"),
271             sortable: true,
272             weight: 10,
273             align: WebInspector.DataGrid.Align.Right
274         });
275
276         columns.push({
277             id: "type",
278             title: WebInspector.UIString("Type"),
279             sortable: true,
280             weight: 6
281         });
282
283         columns.push({
284             id: "initiator",
285             title: WebInspector.UIString("Initiator"),
286             sortable: true,
287             weight: 10
288         });
289
290         columns.push({
291             id: "cookies",
292             title: WebInspector.UIString("Cookies"),
293             sortable: true,
294             weight: 6,
295             align: WebInspector.DataGrid.Align.Right
296         });
297
298         columns.push({
299             id: "setCookies",
300             title: WebInspector.UIString("Set-Cookies"),
301             sortable: true,
302             weight: 6,
303             align: WebInspector.DataGrid.Align.Right
304         });
305
306         columns.push({
307             id: "size",
308             titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
309             title: WebInspector.UIString("Size"),
310             sortable: true,
311             weight: 6,
312             align: WebInspector.DataGrid.Align.Right
313         });
314
315         columns.push({
316             id: "time",
317             titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
318             title: WebInspector.UIString("Time"),
319             sortable: true,
320             weight: 6,
321             align: WebInspector.DataGrid.Align.Right
322         });
323
324         var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
325         for (var i = 0; i < responseHeaderColumns.length; ++i) {
326             var headerName = responseHeaderColumns[i];
327             var descriptor = {
328                 id: headerName,
329                 title: WebInspector.UIString(headerName),
330                 weight: 6
331             }
332             if (headerName === "Content-Length")
333                 descriptor.align = WebInspector.DataGrid.Align.Right;
334             columns.push(descriptor);
335         }
336
337         columns.push({
338             id: "timeline",
339             titleDOMFragment: document.createDocumentFragment(),
340             title: WebInspector.UIString("Timeline"),
341             sortable: false,
342             weight: 40,
343             sort: WebInspector.DataGrid.Order.Ascending
344         });
345
346         this._dataGrid = new WebInspector.DataGrid(columns);
347         this._dataGrid.setName("networkLog");
348         this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
349         this._dataGrid.element.classList.add("network-log-grid");
350         this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
351         this._dataGrid.show(this.element);
352
353         // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
354         this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
355         this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
356
357         this._patchTimelineHeader();
358     },
359
360     _makeHeaderFragment: function(title, subtitle)
361     {
362         var fragment = document.createDocumentFragment();
363         fragment.createTextChild(title);
364         var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
365         subtitleDiv.createTextChild(subtitle);
366         return fragment;
367     },
368
369     _patchTimelineHeader: function()
370     {
371         var timelineSorting = document.createElement("select");
372
373         var option = document.createElement("option");
374         option.value = "startTime";
375         option.label = WebInspector.UIString("Timeline");
376         timelineSorting.appendChild(option);
377
378         option = document.createElement("option");
379         option.value = "startTime";
380         option.label = WebInspector.UIString("Start Time");
381         timelineSorting.appendChild(option);
382
383         option = document.createElement("option");
384         option.value = "responseTime";
385         option.label = WebInspector.UIString("Response Time");
386         timelineSorting.appendChild(option);
387
388         option = document.createElement("option");
389         option.value = "endTime";
390         option.label = WebInspector.UIString("End Time");
391         timelineSorting.appendChild(option);
392
393         option = document.createElement("option");
394         option.value = "duration";
395         option.label = WebInspector.UIString("Duration");
396         timelineSorting.appendChild(option);
397
398         option = document.createElement("option");
399         option.value = "latency";
400         option.label = WebInspector.UIString("Latency");
401         timelineSorting.appendChild(option);
402
403         var header = this._dataGrid.headerTableHeader("timeline");
404         header.replaceChild(timelineSorting, header.firstChild);
405
406         timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
407         timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
408         this._timelineSortSelector = timelineSorting;
409     },
410
411     _createSortingFunctions: function()
412     {
413         this._sortingFunctions = {};
414         this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
415         this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
416         this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
417         this._sortingFunctions.scheme = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "scheme", false);
418         this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
419         this._sortingFunctions.remoteAddress = WebInspector.NetworkDataGridNode.RemoteAddressComparator;
420         this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
421         this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
422         this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
423         this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
424         this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
425         this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
426         this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
427         this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
428         this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
429         this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
430         this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
431         this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
432
433         var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
434         var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
435
436         this._calculators = {};
437         this._calculators.timeline = timeCalculator;
438         this._calculators.startTime = timeCalculator;
439         this._calculators.endTime = timeCalculator;
440         this._calculators.responseTime = timeCalculator;
441         this._calculators.duration = durationCalculator;
442         this._calculators.latency = durationCalculator;
443     },
444
445     _sortItems: function()
446     {
447         this._removeAllNodeHighlights();
448         var columnIdentifier = this._dataGrid.sortColumnIdentifier();
449         if (columnIdentifier === "timeline") {
450             this._sortByTimeline();
451             return;
452         }
453         var sortingFunction = this._sortingFunctions[columnIdentifier];
454         if (!sortingFunction)
455             return;
456
457         this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
458         this._timelineSortSelector.selectedIndex = 0;
459
460         this.searchCanceled();
461
462         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
463             action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
464             column: columnIdentifier,
465             sortOrder: this._dataGrid.sortOrder()
466         });
467     },
468
469     _sortByTimeline: function()
470     {
471         this._removeAllNodeHighlights();
472         var selectedIndex = this._timelineSortSelector.selectedIndex;
473         if (!selectedIndex)
474             selectedIndex = 1; // Sort by start time by default.
475         var selectedOption = this._timelineSortSelector[selectedIndex];
476         var value = selectedOption.value;
477
478         var sortingFunction = this._sortingFunctions[value];
479         this._dataGrid.sortNodes(sortingFunction);
480         this.calculator = this._calculators[value];
481         if (this.calculator.startAtZero)
482             this._timelineGrid.hideEventDividers();
483         else
484             this._timelineGrid.showEventDividers();
485         this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
486     },
487
488     _createStatusBarItems: function()
489     {
490         this._progressBarContainer = document.createElement("div");
491         this._progressBarContainer.className = "status-bar-item";
492     },
493
494     _updateSummaryBar: function()
495     {
496         var requestsNumber = this._requests.length;
497
498         if (!requestsNumber) {
499             if (this._summaryBarElement._isDisplayingWarning)
500                 return;
501             this._summaryBarElement._isDisplayingWarning = true;
502             this._summaryBarElement.removeChildren();
503             this._summaryBarElement.createChild("div", "warning-icon-small");
504             var text = WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.");
505             this._summaryBarElement.appendChild(document.createTextNode(text));
506             this._summaryBarElement.title = text;
507             return;
508         }
509         delete this._summaryBarElement._isDisplayingWarning;
510
511         var transferSize = 0;
512         var selectedRequestsNumber = 0;
513         var selectedTransferSize = 0;
514         var baseTime = -1;
515         var maxTime = -1;
516         for (var i = 0; i < this._requests.length; ++i) {
517             var request = this._requests[i];
518             var requestTransferSize = request.transferSize;
519             transferSize += requestTransferSize;
520             if (!this._filteredOutRequests.get(request)) {
521                 selectedRequestsNumber++;
522                 selectedTransferSize += requestTransferSize;
523             }
524             if (request.url === request.target().resourceTreeModel.inspectedPageURL())
525                 baseTime = request.startTime;
526             if (request.endTime > maxTime)
527                 maxTime = request.endTime;
528         }
529         var text = "";
530         if (selectedRequestsNumber !== requestsNumber) {
531             text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
532             text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
533         } else {
534             text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
535             text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
536         }
537         if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentLoadedTime !== -1 && this._mainRequestDOMContentLoadedTime > baseTime) {
538             text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s (load: %s, DOMContentLoaded: %s)"),
539                         Number.secondsToString(maxTime - baseTime),
540                         Number.secondsToString(this._mainRequestLoadTime - baseTime),
541                         Number.secondsToString(this._mainRequestDOMContentLoadedTime - baseTime));
542         }
543         this._summaryBarElement.textContent = text;
544         this._summaryBarElement.title = text;
545     },
546
547     _scheduleRefresh: function()
548     {
549         if (this._needsRefresh)
550             return;
551
552         this._needsRefresh = true;
553
554         if (this.isShowing() && !this._refreshTimeout)
555             this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
556     },
557
558     _updateDividersIfNeeded: function()
559     {
560         if (!this._dataGrid)
561             return;
562         var timelineColumn = this._dataGrid.columns.timeline;
563         for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
564             if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnIndex) {
565                 // Position timline grid location.
566                 this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
567             }
568         }
569
570         var proceed = true;
571         if (!this.isShowing()) {
572             this._scheduleRefresh();
573             proceed = false;
574         } else {
575             this.calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
576             proceed = this._timelineGrid.updateDividers(this.calculator);
577         }
578         if (!proceed)
579             return;
580
581         if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
582             // If our current sorting method starts at zero, that means it shows all
583             // requests starting at the same point, and so onLoad event and DOMContent
584             // event lines really wouldn't make much sense here, so don't render them.
585             // Additionally, if the calculator doesn't have the computePercentageFromEventTime
586             // function defined, we are probably sorting by size, and event times aren't relevant
587             // in this case.
588             return;
589         }
590
591         this._timelineGrid.removeEventDividers();
592         if (this._mainRequestLoadTime !== -1) {
593             var percent = this.calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
594
595             var loadDivider = document.createElement("div");
596             loadDivider.className = "network-event-divider network-red-divider";
597
598             var loadDividerPadding = document.createElement("div");
599             loadDividerPadding.className = "network-event-divider-padding";
600             loadDividerPadding.title = WebInspector.UIString("Load event");
601             loadDividerPadding.appendChild(loadDivider);
602             loadDividerPadding.style.left = percent + "%";
603             this._timelineGrid.addEventDivider(loadDividerPadding);
604         }
605
606         if (this._mainRequestDOMContentLoadedTime !== -1) {
607             var percent = this.calculator.computePercentageFromEventTime(this._mainRequestDOMContentLoadedTime);
608
609             var domContentLoadedDivider = document.createElement("div");
610             domContentLoadedDivider.className = "network-event-divider network-blue-divider";
611
612             var domContentLoadedDividerPadding = document.createElement("div");
613             domContentLoadedDividerPadding.className = "network-event-divider-padding";
614             domContentLoadedDividerPadding.title = WebInspector.UIString("DOMContentLoaded event");
615             domContentLoadedDividerPadding.appendChild(domContentLoadedDivider);
616             domContentLoadedDividerPadding.style.left = percent + "%";
617             this._timelineGrid.addEventDivider(domContentLoadedDividerPadding);
618         }
619     },
620
621     _refreshIfNeeded: function()
622     {
623         if (this._needsRefresh)
624             this.refresh();
625     },
626
627     _invalidateAllItems: function()
628     {
629         for (var i = 0; i < this._requests.length; ++i) {
630             var request = this._requests[i];
631             this._staleRequests[request.requestId] = request;
632         }
633     },
634
635     get calculator()
636     {
637         return this._calculator;
638     },
639
640     set calculator(x)
641     {
642         if (!x || this._calculator === x)
643             return;
644
645         this._calculator = x;
646         this._calculator.reset();
647
648         this._invalidateAllItems();
649         this.refresh();
650     },
651
652     _requestGridNode: function(request)
653     {
654         return this._requestGridNodes[request.__gridNodeId];
655     },
656
657     _createRequestGridNode: function(request)
658     {
659         var node = new WebInspector.NetworkDataGridNode(this, request);
660         request.__gridNodeId = this._lastRequestGridNodeId++;
661         this._requestGridNodes[request.__gridNodeId] = node;
662         return node;
663     },
664
665     _createStatusbarButtons: function()
666     {
667         this._recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record Network Log"), "record-profile-status-bar-item");
668         this._recordButton.addEventListener("click", this._onRecordButtonClicked, this);
669
670         this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
671         this._clearButton.addEventListener("click", this._reset, this);
672
673         this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
674         this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
675         this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
676
677         this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
678         this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
679
680         this._disableCacheCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Disable cache"));
681         WebInspector.SettingsUI.bindCheckbox(this._disableCacheCheckbox.inputElement, WebInspector.settings.cacheDisabled);
682         this._disableCacheCheckbox.element.title = WebInspector.UIString("Disable cache (while DevTools is open).");
683     },
684
685     _loadEventFired: function(event)
686     {
687         if (!this._recordButton.toggled)
688             return;
689
690         this._mainRequestLoadTime = event.data || -1;
691         // Schedule refresh to update boundaries and draw the new line.
692         this._scheduleRefresh();
693     },
694
695     _domContentLoadedEventFired: function(event)
696     {
697         if (!this._recordButton.toggled)
698             return;
699         this._mainRequestDOMContentLoadedTime = event.data || -1;
700         // Schedule refresh to update boundaries and draw the new line.
701         this._scheduleRefresh();
702     },
703
704     wasShown: function()
705     {
706         this._refreshIfNeeded();
707     },
708
709     willHide: function()
710     {
711         this._popoverHelper.hidePopover();
712     },
713
714     refresh: function()
715     {
716         this._needsRefresh = false;
717         if (this._refreshTimeout) {
718             clearTimeout(this._refreshTimeout);
719             delete this._refreshTimeout;
720         }
721
722         this._removeAllNodeHighlights();
723         var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
724         var boundariesChanged = false;
725         if (this.calculator.updateBoundariesForEventTime) {
726             boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
727             boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime) || boundariesChanged;
728         }
729
730         for (var requestId in this._staleRequests) {
731             var request = this._staleRequests[requestId];
732             var node = this._requestGridNode(request);
733             if (!node) {
734                 // Create the timeline tree element and graph.
735                 node = this._createRequestGridNode(request);
736                 this._dataGrid.rootNode().appendChild(node);
737             }
738             node.refreshRequest();
739             this._applyFilter(node);
740
741             if (this.calculator.updateBoundaries(request))
742                 boundariesChanged = true;
743
744             if (!node.isFilteredOut())
745                 this._updateHighlightIfMatched(request);
746         }
747
748         if (boundariesChanged) {
749             // The boundaries changed, so all item graphs are stale.
750             this._invalidateAllItems();
751         }
752
753         for (var requestId in this._staleRequests)
754             this._requestGridNode(this._staleRequests[requestId]).refreshGraph(this.calculator);
755
756         this._staleRequests = {};
757         this._sortItems();
758         this._updateSummaryBar();
759         this._dataGrid.updateWidths();
760         // FIXME: evaluate performance impact of moving this before a call to sortItems()
761         if (wasScrolledToLastRow)
762             this._dataGrid.scrollToLastRow();
763     },
764
765     _onRecordButtonClicked: function()
766     {
767         if (!this._recordButton.toggled)
768             this._reset();
769         this._recordButton.toggled = !this._recordButton.toggled;
770     },
771
772     _reset: function()
773     {
774         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
775
776         this._clearSearchMatchedList();
777         if (this._popoverHelper)
778             this._popoverHelper.hidePopover();
779
780         if (this._calculator)
781             this._calculator.reset();
782
783         this._requests = [];
784         this._requestsById = {};
785         this._requestsByURL = {};
786         this._staleRequests = {};
787         this._requestGridNodes = {};
788         this._resetSuggestionBuilder();
789
790         if (this._dataGrid) {
791             this._dataGrid.rootNode().removeChildren();
792             this._updateDividersIfNeeded();
793             this._updateSummaryBar();
794         }
795
796         this._mainRequestLoadTime = -1;
797         this._mainRequestDOMContentLoadedTime = -1;
798     },
799
800     get requests()
801     {
802         return this._requests;
803     },
804
805     _onRequestStarted: function(event)
806     {
807         if (this._recordButton.toggled)
808             this._appendRequest(event.data);
809     },
810
811     _appendRequest: function(request)
812     {
813         this._requests.push(request);
814
815         // In case of redirect request id is reassigned to a redirected
816         // request and we need to update _requestsById and search results.
817         if (this._requestsById[request.requestId]) {
818             var oldRequest = request.redirects[request.redirects.length - 1];
819             this._requestsById[oldRequest.requestId] = oldRequest;
820
821             this._updateSearchMatchedListAfterRequestIdChanged(request.requestId, oldRequest.requestId);
822         }
823         this._requestsById[request.requestId] = request;
824
825         this._requestsByURL[request.url] = request;
826
827         // Pull all the redirects of the main request upon commit load.
828         if (request.redirects) {
829             for (var i = 0; i < request.redirects.length; ++i)
830                 this._refreshRequest(request.redirects[i]);
831         }
832
833         this._refreshRequest(request);
834     },
835
836     /**
837      * @param {!WebInspector.Event} event
838      */
839     _onRequestUpdated: function(event)
840     {
841         var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
842         this._refreshRequest(request);
843     },
844
845     /**
846      * @param {!WebInspector.NetworkRequest} request
847      */
848     _refreshRequest: function(request)
849     {
850         if (!this._requestsById[request.requestId])
851             return;
852
853         this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Domain, request.domain);
854         this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.Method, request.requestMethod);
855         this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.MimeType, request.mimeType);
856         this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.StatusCode, "" + request.statusCode);
857
858         var responseHeaders = request.responseHeaders;
859         for (var i = 0, l = responseHeaders.length; i < l; ++i)
860             this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.HasResponseHeader, responseHeaders[i].name);
861         var cookies = request.responseCookies;
862         for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
863             var cookie = cookies[i];
864             this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieDomain, cookie.domain());
865             this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieName, cookie.name());
866             this._suggestionBuilder.addItem(WebInspector.NetworkPanel.FilterType.SetCookieValue, cookie.value());
867         }
868
869         this._staleRequests[request.requestId] = request;
870         this._scheduleRefresh();
871     },
872
873     _willReloadPage: function(event)
874     {
875         this._recordButton.toggled = true;
876         if (!this._preserveLogCheckbox.checked())
877             this._reset();
878     },
879
880     /**
881      * @param {!WebInspector.Event} event
882      */
883     _mainFrameNavigated: function(event)
884     {
885         if (!this._recordButton.toggled || this._preserveLogCheckbox.checked())
886             return;
887
888         var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
889         var loaderId = frame.loaderId;
890
891         // Pick provisional load requests.
892         var requestsToPick = [];
893         var requests = frame.target().networkLog.requests;
894         for (var i = 0; i < requests.length; ++i) {
895             var request = requests[i];
896             if (request.loaderId === loaderId)
897                 requestsToPick.push(request);
898         }
899
900         this._reset();
901
902         for (var i = 0; i < requestsToPick.length; ++i)
903             this._appendRequest(requestsToPick[i]);
904     },
905
906     switchToDetailedView: function()
907     {
908         if (!this._dataGrid)
909             return;
910         if (this._dataGrid.selectedNode)
911             this._dataGrid.selectedNode.selected = false;
912
913         this.element.classList.remove("brief-mode");
914         this._detailedMode = true;
915         this._updateColumns();
916     },
917
918     switchToBriefView: function()
919     {
920         this.element.classList.add("brief-mode");
921         this._removeAllNodeHighlights();
922         this._detailedMode = false;
923         this._updateColumns();
924         this._popoverHelper.hidePopover();
925     },
926
927     _toggleLargerRequests: function()
928     {
929         WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
930         this._setLargerRequests(WebInspector.settings.resourcesLargeRows.get());
931     },
932
933     _setLargerRequests: function(enabled)
934     {
935         this._largerRequestsButton.toggled = enabled;
936         if (!enabled) {
937             this._largerRequestsButton.title = WebInspector.UIString("Use large resource rows.");
938             this._dataGrid.element.classList.add("small");
939             this._timelineGrid.element.classList.add("small");
940         } else {
941             this._largerRequestsButton.title = WebInspector.UIString("Use small resource rows.");
942             this._dataGrid.element.classList.remove("small");
943             this._timelineGrid.element.classList.remove("small");
944         }
945         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
946     },
947
948     _getPopoverAnchor: function(element)
949     {
950         if (!this._allowPopover)
951             return;
952         var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
953         if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
954             return anchor;
955         anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
956         if (anchor && anchor.request && anchor.request.initiator)
957             return anchor;
958
959         return null;
960     },
961
962     /**
963      * @param {!Element} anchor
964      * @param {!WebInspector.Popover} popover
965      */
966     _showPopover: function(anchor, popover)
967     {
968         var content;
969         if (anchor.classList.contains("network-script-initiated"))
970             content = this._generateScriptInitiatedPopoverContent(anchor.request);
971         else
972             content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
973         popover.show(content, anchor);
974     },
975
976     _onHidePopover: function()
977     {
978         this._linkifier.reset();
979     },
980
981     /**
982      * @param {!WebInspector.NetworkRequest} request
983      * @return {!Element}
984      */
985     _generateScriptInitiatedPopoverContent: function(request)
986     {
987         var stackTrace = request.initiator.stackTrace;
988         var framesTable = document.createElement("table");
989         for (var i = 0; i < stackTrace.length; ++i) {
990             var stackFrame = stackTrace[i];
991             var row = document.createElement("tr");
992             row.createChild("td").textContent = stackFrame.functionName || WebInspector.UIString("(anonymous function)");
993             row.createChild("td").textContent = " @ ";
994             row.createChild("td").appendChild(this._linkifier.linkifyLocation(request.target(), stackFrame.url, stackFrame.lineNumber - 1, stackFrame.columnNumber - 1));
995             framesTable.appendChild(row);
996         }
997         return framesTable;
998     },
999
1000     _updateColumns: function()
1001     {
1002         var columnsVisibility = this._coulmnsVisibilitySetting.get();
1003         var detailedMode = !!this._detailedMode;
1004         for (var columnIdentifier in columnsVisibility) {
1005             var visible = detailedMode && columnsVisibility[columnIdentifier];
1006             this._dataGrid.setColumnVisible(columnIdentifier, visible);
1007         }
1008         this._dataGrid.setColumnVisible("timeline", detailedMode);
1009         this._dataGrid.applyColumnWeights();
1010     },
1011
1012     /**
1013      * @param {string} columnIdentifier
1014      */
1015     _toggleColumnVisibility: function(columnIdentifier)
1016     {
1017         var columnsVisibility = this._coulmnsVisibilitySetting.get();
1018         columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
1019         this._coulmnsVisibilitySetting.set(columnsVisibility);
1020
1021         this._updateColumns();
1022     },
1023
1024     /**
1025      * @return {!Array.<string>}
1026      */
1027     _getConfigurableColumnIDs: function()
1028     {
1029         if (this._configurableColumnIDs)
1030             return this._configurableColumnIDs;
1031
1032         var columns = this._dataGrid.columns;
1033         function compare(id1, id2)
1034         {
1035             return columns[id1].title.compareTo(columns[id2].title);
1036         }
1037
1038         var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
1039         this._configurableColumnIDs = columnIDs.sort(compare);
1040         return this._configurableColumnIDs;
1041     },
1042
1043     _contextMenu: function(event)
1044     {
1045         var contextMenu = new WebInspector.ContextMenu(event);
1046
1047         if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
1048             var columnsVisibility = this._coulmnsVisibilitySetting.get();
1049             var columnIDs = this._getConfigurableColumnIDs();
1050             for (var i = 0; i < columnIDs.length; ++i) {
1051                 var columnIdentifier = columnIDs[i];
1052                 var column = this._dataGrid.columns[columnIdentifier];
1053                 contextMenu.appendCheckboxItem(column.title, this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
1054             }
1055             contextMenu.show();
1056             return;
1057         }
1058
1059         var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
1060         var request = gridNode && gridNode._request;
1061
1062         /**
1063          * @param {string} url
1064          */
1065         function openResourceInNewTab(url)
1066         {
1067             InspectorFrontendHost.openInNewTab(url);
1068         }
1069
1070         if (request) {
1071             contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), openResourceInNewTab.bind(null, request.url));
1072             contextMenu.appendSeparator();
1073             contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
1074             if (request.requestHeadersText())
1075                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
1076             if (request.responseHeadersText)
1077                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
1078             if (request.finished)
1079                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response" : "Copy Response"), this._copyResponse.bind(this, request));
1080             contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
1081         }
1082         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
1083
1084         contextMenu.appendSeparator();
1085         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
1086
1087         contextMenu.appendSeparator();
1088         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
1089         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
1090
1091         if (request && request.type === WebInspector.resourceTypes.XHR) {
1092             contextMenu.appendSeparator();
1093             contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
1094             contextMenu.appendSeparator();
1095         }
1096
1097         contextMenu.show();
1098     },
1099
1100     _replayXHR: function(requestId)
1101     {
1102         NetworkAgent.replayXHR(requestId);
1103     },
1104
1105     _harRequests: function()
1106     {
1107         var httpRequests = this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1108         httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1109         return httpRequests.filter(WebInspector.NetworkLogView.NonSourceMapRequestsFilter);
1110     },
1111
1112     _copyAll: function()
1113     {
1114         var harArchive = {
1115             log: (new WebInspector.HARLog(this._harRequests())).build()
1116         };
1117         InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1118     },
1119
1120     _copyLocation: function(request)
1121     {
1122         InspectorFrontendHost.copyText(request.url);
1123     },
1124
1125     _copyRequestHeaders: function(request)
1126     {
1127         InspectorFrontendHost.copyText(request.requestHeadersText());
1128     },
1129
1130     _copyResponse: function(request)
1131     {
1132         function callback(content)
1133         {
1134             if (request.contentEncoded)
1135                 content = request.asDataURL();
1136             InspectorFrontendHost.copyText(content || "");
1137         }
1138         request.requestContent(callback);
1139     },
1140
1141     _copyResponseHeaders: function(request)
1142     {
1143         InspectorFrontendHost.copyText(request.responseHeadersText);
1144     },
1145
1146     /**
1147      * @param {!WebInspector.NetworkRequest} request
1148      */
1149     _copyCurlCommand: function(request)
1150     {
1151         InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1152     },
1153
1154     _exportAll: function()
1155     {
1156         var filename = WebInspector.resourceTreeModel.inspectedPageDomain() + ".har";
1157         var stream = new WebInspector.FileOutputStream();
1158         stream.open(filename, openCallback.bind(this));
1159
1160         /**
1161          * @param {boolean} accepted
1162          * @this {WebInspector.NetworkLogView}
1163          */
1164         function openCallback(accepted)
1165         {
1166             if (!accepted)
1167                 return;
1168             var progressIndicator = new WebInspector.ProgressIndicator();
1169             this._progressBarContainer.appendChild(progressIndicator.element);
1170             var harWriter = new WebInspector.HARWriter();
1171             harWriter.write(stream, this._harRequests(), progressIndicator);
1172         }
1173     },
1174
1175     _clearBrowserCache: function()
1176     {
1177         if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1178             NetworkAgent.clearBrowserCache();
1179     },
1180
1181     _clearBrowserCookies: function()
1182     {
1183         if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1184             NetworkAgent.clearBrowserCookies();
1185     },
1186
1187     _matchRequest: function(request)
1188     {
1189         if (!this._searchRegExp)
1190             return -1;
1191
1192         if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp))
1193             return -1;
1194
1195         if (request.requestId in this._matchedRequestsMap)
1196             return this._matchedRequestsMap[request.requestId];
1197
1198         var matchedRequestIndex = this._matchedRequests.length;
1199         this._matchedRequestsMap[request.requestId] = matchedRequestIndex;
1200         this._matchedRequests.push(request.requestId);
1201
1202         return matchedRequestIndex;
1203     },
1204
1205     _clearSearchMatchedList: function()
1206     {
1207         delete this._searchRegExp;
1208         this._matchedRequests = [];
1209         this._matchedRequestsMap = {};
1210         this._removeAllHighlights();
1211     },
1212
1213     _updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
1214     {
1215         var requestIndex = this._matchedRequestsMap[oldRequestId];
1216         if (requestIndex) {
1217             delete this._matchedRequestsMap[oldRequestId];
1218             this._matchedRequestsMap[newRequestId] = requestIndex;
1219             this._matchedRequests[requestIndex] = newRequestId;
1220         }
1221     },
1222
1223     _updateHighlightIfMatched: function(request)
1224     {
1225         var matchedRequestIndex = this._matchRequest(request);
1226         if (matchedRequestIndex === -1)
1227             return;
1228
1229         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1230
1231         if (this._currentMatchedRequestIndex !== -1 && this._currentMatchedRequestIndex !== matchedRequestIndex)
1232             return;
1233
1234         this._highlightNthMatchedRequestForSearch(matchedRequestIndex, false);
1235     },
1236
1237     _removeAllHighlights: function()
1238     {
1239         this._removeAllNodeHighlights();
1240         for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1241             WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1242         this._highlightedSubstringChanges = [];
1243     },
1244
1245     /**
1246      * @param {!WebInspector.NetworkRequest} request
1247      * @param {boolean} reveal
1248      * @param {!RegExp=} regExp
1249      */
1250     _highlightMatchedRequest: function(request, reveal, regExp)
1251     {
1252         var node = this._requestGridNode(request);
1253         if (!node)
1254             return;
1255
1256         var nameMatched = request.name().match(regExp);
1257         var pathMatched = request.path().match(regExp);
1258         if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1259             this._toggleLargerRequests();
1260         var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
1261         this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1262         if (reveal) {
1263             node.reveal();
1264             this._highlightNode(node);
1265         }
1266     },
1267
1268     /**
1269      * @param {number} matchedRequestIndex
1270      * @param {boolean} reveal
1271      */
1272     _highlightNthMatchedRequestForSearch: function(matchedRequestIndex, reveal)
1273     {
1274         var request = this._requestsById[this._matchedRequests[matchedRequestIndex]];
1275         if (!request)
1276             return;
1277         this._removeAllHighlights();
1278         this._highlightMatchedRequest(request, reveal, this._searchRegExp);
1279         var node = this._requestGridNode(request);
1280         if (node)
1281             this._currentMatchedRequestIndex = matchedRequestIndex;
1282
1283         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedRequestIndex);
1284     },
1285
1286     /**
1287      * @param {string} query
1288      * @param {boolean} shouldJump
1289      * @param {boolean=} jumpBackwards
1290      */
1291     performSearch: function(query, shouldJump, jumpBackwards)
1292     {
1293         var newMatchedRequestIndex = jumpBackwards ? -1 : 0;
1294         var currentMatchedRequestId;
1295         if (this._currentMatchedRequestIndex !== -1)
1296             currentMatchedRequestId = this._matchedRequests[this._currentMatchedRequestIndex];
1297
1298         this._clearSearchMatchedList();
1299         this._searchRegExp = createPlainTextSearchRegex(query, "i");
1300
1301         var childNodes = this._dataGrid.dataTableBody.childNodes;
1302         var requestNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
1303
1304         for (var i = 0; i < requestNodes.length; ++i) {
1305             var dataGridNode = this._dataGrid.dataGridNodeFromNode(requestNodes[i]);
1306             if (dataGridNode.isFilteredOut())
1307                 continue;
1308             if (this._matchRequest(dataGridNode._request) !== -1 && dataGridNode._request.requestId === currentMatchedRequestId) {
1309                 // Keep current search result the same if it still matches.
1310                 newMatchedRequestIndex = this._matchedRequests.length - 1;
1311             }
1312         }
1313
1314         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
1315         if (shouldJump) {
1316             var index = this._normalizeSearchResultIndex(newMatchedRequestIndex);
1317             this._highlightNthMatchedRequestForSearch(index, true);
1318         }
1319     },
1320
1321     _normalizeSearchResultIndex: function(index)
1322     {
1323         return (index + this._matchedRequests.length) % this._matchedRequests.length;
1324     },
1325
1326     /**
1327      * @param {!WebInspector.NetworkDataGridNode} node
1328      */
1329     _applyFilter: function(node)
1330     {
1331         var request = node._request;
1332         var matches = this._resourceTypeFilterUI.accept(request.type.name());
1333         if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1334             matches = false;
1335         for (var i = 0; matches && (i < this._filters.length); ++i)
1336             matches = this._filters[i](request);
1337
1338         node.element.classList.toggle("filtered-out", !matches);
1339         if (matches)
1340             this._filteredOutRequests.remove(request);
1341         else
1342             this._filteredOutRequests.put(request, true);
1343     },
1344
1345     /**
1346      * @param {string} query
1347      */
1348     _parseFilterQuery: function(query)
1349     {
1350         var parsedQuery = this._suggestionBuilder.parseQuery(query);
1351         this._filters = parsedQuery.text.map(this._createTextFilter);
1352         for (var key in parsedQuery.filters) {
1353             var filterType = /** @type {!WebInspector.NetworkPanel.FilterType} */ (key);
1354             this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1355         }
1356     },
1357
1358     /**
1359      * @param {string} text
1360      * @return {!WebInspector.NetworkLogView.Filter}
1361      */
1362     _createTextFilter: function(text)
1363     {
1364         var regexp = new RegExp(text.escapeForRegExp(), "i");
1365         return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1366     },
1367
1368     /**
1369      * @param {!WebInspector.NetworkPanel.FilterType} type
1370      * @param {string} value
1371      * @return {!WebInspector.NetworkLogView.Filter}
1372      */
1373     _createFilter: function(type, value) {
1374         switch (type) {
1375         case WebInspector.NetworkPanel.FilterType.Domain:
1376             return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1377
1378         case WebInspector.NetworkPanel.FilterType.HasResponseHeader:
1379             return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1380
1381         case WebInspector.NetworkPanel.FilterType.Method:
1382             return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1383
1384         case WebInspector.NetworkPanel.FilterType.MimeType:
1385             return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1386
1387         case WebInspector.NetworkPanel.FilterType.SetCookieDomain:
1388             return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1389
1390         case WebInspector.NetworkPanel.FilterType.SetCookieName:
1391             return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1392
1393         case WebInspector.NetworkPanel.FilterType.SetCookieValue:
1394             return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1395
1396         case WebInspector.NetworkPanel.FilterType.StatusCode:
1397             return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1398         }
1399         return this._createTextFilter(type + ":" + value);
1400     },
1401
1402     _filterRequests: function()
1403     {
1404         this._removeAllHighlights();
1405         this._filteredOutRequests.clear();
1406
1407         var nodes = this._dataGrid.rootNode().children;
1408         for (var i = 0; i < nodes.length; ++i)
1409             this._applyFilter(nodes[i]);
1410         this._updateSummaryBar();
1411     },
1412
1413     jumpToPreviousSearchResult: function()
1414     {
1415         if (!this._matchedRequests.length)
1416             return;
1417         var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1418         this._highlightNthMatchedRequestForSearch(index, true);
1419     },
1420
1421     jumpToNextSearchResult: function()
1422     {
1423         if (!this._matchedRequests.length)
1424             return;
1425         var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1426         this._highlightNthMatchedRequestForSearch(index, true);
1427     },
1428
1429     searchCanceled: function()
1430     {
1431         this._clearSearchMatchedList();
1432         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1433     },
1434
1435     revealAndHighlightRequest: function(request)
1436     {
1437         this._removeAllNodeHighlights();
1438
1439         var node = this._requestGridNode(request);
1440         if (node) {
1441             this._dataGrid.element.focus();
1442             node.reveal();
1443             this._highlightNode(node);
1444         }
1445     },
1446
1447     _removeAllNodeHighlights: function()
1448     {
1449         if (this._highlightedNode) {
1450             this._highlightedNode.element.classList.remove("highlighted-row");
1451             delete this._highlightedNode;
1452         }
1453     },
1454
1455     _highlightNode: function(node)
1456     {
1457         WebInspector.runCSSAnimationOnce(node.element, "highlighted-row");
1458         this._highlightedNode = node;
1459     },
1460
1461    /**
1462      * @param {!WebInspector.NetworkRequest} request
1463      * @return {string}
1464      */
1465     _generateCurlCommand: function(request)
1466     {
1467         var command = ["curl"];
1468         // These headers are derived from URL (except "version") and would be added by cURL anyway.
1469         var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1470
1471         function escapeStringWin(str)
1472         {
1473             /* Replace quote by double quote (but not by \") because it is
1474                recognized by both cmd.exe and MS Crt arguments parser.
1475
1476                Replace % by "%" because it could be expanded to an environment
1477                variable value. So %% becomes "%""%". Even if an env variable ""
1478                (2 doublequotes) is declared, the cmd.exe will not
1479                substitute it with its value.
1480
1481                Replace each backslash with double backslash to make sure
1482                MS Crt arguments parser won't collapse them.
1483
1484                Replace new line outside of quotes since cmd.exe doesn't let
1485                to do it inside.
1486             */
1487             return "\"" + str.replace(/"/g, "\"\"")
1488                              .replace(/%/g, "\"%\"")
1489                              .replace(/\\/g, "\\\\")
1490                              .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1491         }
1492
1493         function escapeStringPosix(str)
1494         {
1495             function escapeCharacter(x)
1496             {
1497                 var code = x.charCodeAt(0);
1498                 if (code < 256) {
1499                     // Add leading zero when needed to not care about the next character.
1500                     return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1501                  }
1502                  code = code.toString(16);
1503                  return "\\u" + ("0000" + code).substr(code.length, 4);
1504              }
1505
1506             if (/[^\x20-\x7E]|\'/.test(str)) {
1507                 // Use ANSI-C quoting syntax.
1508                 return "$\'" + str.replace(/\\/g, "\\\\")
1509                                   .replace(/\'/g, "\\\'")
1510                                   .replace(/\n/g, "\\n")
1511                                   .replace(/\r/g, "\\r")
1512                                   .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1513             } else {
1514                 // Use single quote syntax.
1515                 return "'" + str + "'";
1516             }
1517         }
1518
1519         // cURL command expected to run on the same platform that DevTools run
1520         // (it may be different from the inspected page platform).
1521         var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1522
1523         command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1524
1525         var inferredMethod = "GET";
1526         var data = [];
1527         var requestContentType = request.requestContentType();
1528         if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1529            data.push("--data");
1530            data.push(escapeString(request.requestFormData));
1531            ignoredHeaders["content-length"] = true;
1532            inferredMethod = "POST";
1533         } else if (request.requestFormData) {
1534            data.push("--data-binary");
1535            data.push(escapeString(request.requestFormData));
1536            ignoredHeaders["content-length"] = true;
1537            inferredMethod = "POST";
1538         }
1539
1540         if (request.requestMethod !== inferredMethod) {
1541             command.push("-X");
1542             command.push(request.requestMethod);
1543         }
1544
1545         var requestHeaders = request.requestHeaders();
1546         for (var i = 0; i < requestHeaders.length; i++) {
1547             var header = requestHeaders[i];
1548             var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1549             if (name.toLowerCase() in ignoredHeaders)
1550                 continue;
1551             command.push("-H");
1552             command.push(escapeString(name + ": " + header.value));
1553         }
1554         command = command.concat(data);
1555         command.push("--compressed");
1556         return command.join(" ");
1557     },
1558
1559     __proto__: WebInspector.VBox.prototype
1560 }
1561
1562 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1563 WebInspector.NetworkLogView.Filter;
1564
1565 /**
1566  * @param {!RegExp} regex
1567  * @param {!WebInspector.NetworkRequest} request
1568  * @return {boolean}
1569  */
1570 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1571 {
1572     return regex.test(request.name()) || regex.test(request.path());
1573 }
1574
1575 /**
1576  * @param {string} value
1577  * @param {!WebInspector.NetworkRequest} request
1578  * @return {boolean}
1579  */
1580 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1581 {
1582     return request.domain === value;
1583 }
1584
1585 /**
1586  * @param {string} value
1587  * @param {!WebInspector.NetworkRequest} request
1588  * @return {boolean}
1589  */
1590 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1591 {
1592     return request.responseHeaderValue(value) !== undefined;
1593 }
1594
1595 /**
1596  * @param {string} value
1597  * @param {!WebInspector.NetworkRequest} request
1598  * @return {boolean}
1599  */
1600 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1601 {
1602     return request.requestMethod === value;
1603 }
1604
1605 /**
1606  * @param {string} value
1607  * @param {!WebInspector.NetworkRequest} request
1608  * @return {boolean}
1609  */
1610 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1611 {
1612     return request.mimeType === value;
1613 }
1614
1615 /**
1616  * @param {string} value
1617  * @param {!WebInspector.NetworkRequest} request
1618  * @return {boolean}
1619  */
1620 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1621 {
1622     var cookies = request.responseCookies;
1623     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1624         if (cookies[i].domain() === value)
1625             return false;
1626     }
1627     return false;
1628 }
1629
1630 /**
1631  * @param {string} value
1632  * @param {!WebInspector.NetworkRequest} request
1633  * @return {boolean}
1634  */
1635 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1636 {
1637     var cookies = request.responseCookies;
1638     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1639         if (cookies[i].name() === value)
1640             return false;
1641     }
1642     return false;
1643 }
1644
1645 /**
1646  * @param {string} value
1647  * @param {!WebInspector.NetworkRequest} request
1648  * @return {boolean}
1649  */
1650 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1651 {
1652     var cookies = request.responseCookies;
1653     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1654         if (cookies[i].value() === value)
1655             return false;
1656     }
1657     return false;
1658 }
1659
1660 /**
1661  * @param {string} value
1662  * @param {!WebInspector.NetworkRequest} request
1663  * @return {boolean}
1664  */
1665 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1666 {
1667     return ("" + request.statusCode) === value;
1668 }
1669
1670 /**
1671  * @param {!WebInspector.NetworkRequest} request
1672  * @return {boolean}
1673  */
1674 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1675 {
1676     return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1677 }
1678
1679 /**
1680  * @param {!WebInspector.NetworkRequest} request
1681  * @return {boolean}
1682  */
1683 WebInspector.NetworkLogView.NonSourceMapRequestsFilter = function(request)
1684 {
1685     return !WebInspector.SourceMap.hasSourceMapRequestHeader(request);
1686 }
1687
1688 /**
1689  * @param {!WebInspector.NetworkRequest} request
1690  * @return {boolean}
1691  */
1692 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1693 {
1694     return request.finished;
1695 }
1696
1697 WebInspector.NetworkLogView.EventTypes = {
1698     ViewCleared: "ViewCleared",
1699     RowSizeChanged: "RowSizeChanged",
1700     RequestSelected: "RequestSelected",
1701     SearchCountUpdated: "SearchCountUpdated",
1702     SearchIndexUpdated: "SearchIndexUpdated"
1703 };
1704
1705 /**
1706  * @constructor
1707  * @implements {WebInspector.ContextMenu.Provider}
1708  * @implements {WebInspector.Searchable}
1709  * @extends {WebInspector.Panel}
1710  */
1711 WebInspector.NetworkPanel = function()
1712 {
1713     WebInspector.Panel.call(this, "network");
1714     this.registerRequiredCSS("networkPanel.css");
1715     this._injectStyles();
1716
1717     this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1718     this._filterBar = new WebInspector.FilterBar();
1719     this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1720     this._filtersContainer.appendChild(this._filterBar.filtersElement());
1721     this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1722     this._filterBar.setName("networkPanel");
1723
1724     this._searchableView = new WebInspector.SearchableView(this);
1725     this._searchableView.show(this.element);
1726     this._contentsElement = this._searchableView.element;
1727
1728     this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1729     this._splitView.show(this._contentsElement);
1730     this._splitView.hideMain();
1731
1732     var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisibility;
1733     var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1734     var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1735     var columnsVisibility = {};
1736     for (var columnId in defaultColumnsVisibility)
1737         columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1738     networkLogColumnsVisibilitySetting.set(columnsVisibility);
1739
1740     this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1741     this._networkLogView.show(this._splitView.sidebarElement());
1742
1743     var viewsContainerView = new WebInspector.VBox();
1744     this._viewsContainerElement = viewsContainerView.element;
1745     this._viewsContainerElement.id = "network-views";
1746     if (!this._networkLogView.useLargeRows)
1747         this._viewsContainerElement.classList.add("small");
1748     viewsContainerView.show(this._splitView.mainElement());
1749
1750     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1751     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1752     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1753     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1754     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1755
1756     this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1757     this._closeButtonElement.id = "network-close-button";
1758     this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1759     this._viewsContainerElement.appendChild(this._closeButtonElement);
1760
1761     for (var i = 0; i < this._networkLogView.statusBarItems.length; ++i)
1762         this._panelStatusBarElement.appendChild(this._networkLogView.statusBarItems[i]);
1763
1764     /**
1765      * @this {WebInspector.NetworkPanel}
1766      * @return {?WebInspector.SourceFrame}
1767      */
1768     function sourceFrameGetter()
1769     {
1770         return this._networkItemView.currentSourceFrame();
1771     }
1772     WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1773 }
1774
1775 /** @enum {string} */
1776 WebInspector.NetworkPanel.FilterType = {
1777     Domain: "Domain",
1778     HasResponseHeader: "HasResponseHeader",
1779     Method: "Method",
1780     MimeType: "MimeType",
1781     SetCookieDomain: "SetCookieDomain",
1782     SetCookieName: "SetCookieName",
1783     SetCookieValue: "SetCookieValue",
1784     StatusCode: "StatusCode"
1785 };
1786
1787 /** @type {!Array.<string>} */
1788 WebInspector.NetworkPanel._searchKeys = Object.values(WebInspector.NetworkPanel.FilterType);
1789
1790 WebInspector.NetworkPanel.prototype = {
1791     _onFiltersToggled: function(event)
1792     {
1793         var toggled = /** @type {boolean} */ (event.data);
1794         this._filtersContainer.classList.toggle("hidden", !toggled);
1795         this.element.classList.toggle("filters-toggled", toggled);
1796         this.doResize();
1797     },
1798
1799     /**
1800      * @return {!Array.<!Element>}
1801      */
1802     elementsToRestoreScrollPositionsFor: function()
1803     {
1804         return this._networkLogView.elementsToRestoreScrollPositionsFor();
1805     },
1806
1807     /**
1808      * @return {!WebInspector.SearchableView}
1809      */
1810     searchableView: function()
1811     {
1812         return this._searchableView;
1813     },
1814
1815     // FIXME: only used by the layout tests, should not be exposed.
1816     _reset: function()
1817     {
1818         this._networkLogView._reset();
1819     },
1820
1821     handleShortcut: function(event)
1822     {
1823         if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1824             this._toggleGridMode();
1825             event.handled = true;
1826             return;
1827         }
1828
1829         WebInspector.Panel.prototype.handleShortcut.call(this, event);
1830     },
1831
1832     wasShown: function()
1833     {
1834         WebInspector.Panel.prototype.wasShown.call(this);
1835     },
1836
1837     get requests()
1838     {
1839         return this._networkLogView.requests;
1840     },
1841
1842     /**
1843      * @param {!WebInspector.NetworkRequest} request
1844      */
1845     revealAndHighlightRequest: function(request)
1846     {
1847         this._toggleGridMode();
1848         if (request)
1849             this._networkLogView.revealAndHighlightRequest(request);
1850     },
1851
1852     _onViewCleared: function(event)
1853     {
1854         this._closeVisibleRequest();
1855         this._toggleGridMode();
1856         this._viewsContainerElement.removeChildren();
1857         this._viewsContainerElement.appendChild(this._closeButtonElement);
1858     },
1859
1860     _onRowSizeChanged: function(event)
1861     {
1862         this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1863     },
1864
1865     _onSearchCountUpdated: function(event)
1866     {
1867         this._searchableView.updateSearchMatchesCount(event.data);
1868     },
1869
1870     _onSearchIndexUpdated: function(event)
1871     {
1872         this._searchableView.updateCurrentMatchIndex(event.data);
1873     },
1874
1875     _onRequestSelected: function(event)
1876     {
1877         this._showRequest(event.data);
1878     },
1879
1880     /**
1881      * @param {?WebInspector.NetworkRequest} request
1882      */
1883     _showRequest: function(request)
1884     {
1885         if (!request)
1886             return;
1887
1888         this._toggleViewingRequestMode();
1889
1890         if (this._networkItemView) {
1891             this._networkItemView.detach();
1892             delete this._networkItemView;
1893         }
1894
1895         var view = new WebInspector.NetworkItemView(request);
1896         view.show(this._viewsContainerElement);
1897         this._networkItemView = view;
1898     },
1899
1900     _closeVisibleRequest: function()
1901     {
1902         this.element.classList.remove("viewing-resource");
1903
1904         if (this._networkItemView) {
1905             this._networkItemView.detach();
1906             delete this._networkItemView;
1907         }
1908     },
1909
1910     _toggleGridMode: function()
1911     {
1912         if (this._viewingRequestMode) {
1913             this._viewingRequestMode = false;
1914             this.element.classList.remove("viewing-resource");
1915             this._splitView.hideMain();
1916         }
1917
1918         this._networkLogView.switchToDetailedView();
1919         this._networkLogView.allowPopover = true;
1920         this._networkLogView._allowRequestSelection = false;
1921     },
1922
1923     _toggleViewingRequestMode: function()
1924     {
1925         if (this._viewingRequestMode)
1926             return;
1927         this._viewingRequestMode = true;
1928
1929         this.element.classList.add("viewing-resource");
1930         this._splitView.showBoth();
1931         this._networkLogView.allowPopover = false;
1932         this._networkLogView._allowRequestSelection = true;
1933         this._networkLogView.switchToBriefView();
1934     },
1935
1936     /**
1937      * @param {string} query
1938      * @param {boolean} shouldJump
1939      * @param {boolean=} jumpBackwards
1940      */
1941     performSearch: function(query, shouldJump, jumpBackwards)
1942     {
1943         this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
1944     },
1945
1946     jumpToPreviousSearchResult: function()
1947     {
1948         this._networkLogView.jumpToPreviousSearchResult();
1949     },
1950
1951     jumpToNextSearchResult: function()
1952     {
1953         this._networkLogView.jumpToNextSearchResult();
1954     },
1955
1956     searchCanceled: function()
1957     {
1958         this._networkLogView.searchCanceled();
1959     },
1960
1961     /**
1962      * @param {!WebInspector.ContextMenu} contextMenu
1963      * @param {!Object} target
1964      * @this {WebInspector.NetworkPanel}
1965      */
1966     appendApplicableItems: function(event, contextMenu, target)
1967     {
1968         /**
1969          * @this {WebInspector.NetworkPanel}
1970          */
1971         function reveal(request)
1972         {
1973             WebInspector.inspectorView.setCurrentPanel(this);
1974             this.revealAndHighlightRequest(request);
1975         }
1976
1977         /**
1978          * @this {WebInspector.NetworkPanel}
1979          */
1980         function appendRevealItem(request)
1981         {
1982             var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
1983             contextMenu.appendItem(revealText, reveal.bind(this, request));
1984         }
1985
1986         if (target instanceof WebInspector.Resource) {
1987             var resource = /** @type {!WebInspector.Resource} */ (target);
1988             if (resource.request)
1989                 appendRevealItem.call(this, resource.request);
1990             return;
1991         }
1992         if (target instanceof WebInspector.UISourceCode) {
1993             var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
1994             var resource = WebInspector.resourceForURL(uiSourceCode.url);
1995             if (resource && resource.request)
1996                 appendRevealItem.call(this, resource.request);
1997             return;
1998         }
1999
2000         if (!(target instanceof WebInspector.NetworkRequest))
2001             return;
2002         var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2003         if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2004             return;
2005
2006         appendRevealItem.call(this, request);
2007     },
2008
2009     _injectStyles: function()
2010     {
2011         var style = document.createElement("style");
2012         var rules = [];
2013
2014         var columns = WebInspector.NetworkLogView._defaultColumnsVisibility;
2015
2016         var hideSelectors = [];
2017         var bgSelectors = [];
2018         for (var columnId in columns) {
2019             hideSelectors.push("#network-container .hide-" + columnId + "-column ." + columnId + "-column");
2020             bgSelectors.push(".network-log-grid.data-grid td." + columnId + "-column");
2021         }
2022         rules.push(hideSelectors.join(", ") + "{border-left: 0 none transparent;}");
2023         rules.push(bgSelectors.join(", ") + "{background-color: rgba(0, 0, 0, 0.07);}");
2024
2025         style.textContent = rules.join("\n");
2026         document.head.appendChild(style);
2027     },
2028
2029     __proto__: WebInspector.Panel.prototype
2030 }
2031
2032 /**
2033  * @constructor
2034  * @implements {WebInspector.ContextMenu.Provider}
2035  */
2036 WebInspector.NetworkPanel.ContextMenuProvider = function()
2037 {
2038 }
2039
2040 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2041     /**
2042      * @param {!WebInspector.ContextMenu} contextMenu
2043      * @param {!Object} target
2044      */
2045     appendApplicableItems: function(event, contextMenu, target)
2046     {
2047         WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2048     }
2049 }
2050
2051 /**
2052  * @constructor
2053  * @implements {WebInspector.Revealer}
2054  */
2055 WebInspector.NetworkPanel.RequestRevealer = function()
2056 {
2057 }
2058
2059 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2060     /**
2061      * @param {!Object} request
2062      */
2063     reveal: function(request)
2064     {
2065         if (request instanceof WebInspector.NetworkRequest)
2066             /** @type {!WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network")).revealAndHighlightRequest(request);
2067     }
2068 }
2069
2070 /**
2071  * @constructor
2072  * @implements {WebInspector.TimelineGrid.Calculator}
2073  */
2074 WebInspector.NetworkBaseCalculator = function()
2075 {
2076 }
2077
2078 WebInspector.NetworkBaseCalculator.prototype = {
2079     /**
2080      * @param {number} time
2081      * @return {number}
2082      */
2083     computePosition: function(time)
2084     {
2085         return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2086     },
2087
2088     /**
2089      * @return {!{start: number, middle: number, end: number}}
2090      */
2091     computeBarGraphPercentages: function(item)
2092     {
2093         return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
2094     },
2095
2096     /**
2097      * @return {!{left: string, right: string, tooltip: string}}
2098      */
2099     computeBarGraphLabels: function(item)
2100     {
2101         const label = this.formatTime(this._value(item));
2102         return {left: label, right: label, tooltip: label};
2103     },
2104
2105     /**
2106      * @return {number}
2107      */
2108     boundarySpan: function()
2109     {
2110         return this._maximumBoundary - this._minimumBoundary;
2111     },
2112
2113     /**
2114      * @return {boolean}
2115      */
2116     updateBoundaries: function(item)
2117     {
2118         this._minimumBoundary = 0;
2119
2120         var value = this._value(item);
2121         if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
2122             this._maximumBoundary = value;
2123             return true;
2124         }
2125         return false;
2126     },
2127
2128     reset: function()
2129     {
2130         delete this._minimumBoundary;
2131         delete this._maximumBoundary;
2132     },
2133
2134     /**
2135      * @return {number}
2136      */
2137     maximumBoundary: function()
2138     {
2139         return this._maximumBoundary;
2140     },
2141
2142     /**
2143      * @return {number}
2144      */
2145     minimumBoundary: function()
2146     {
2147         return this._minimumBoundary;
2148     },
2149
2150     /**
2151      * @return {number}
2152      */
2153     zeroTime: function()
2154     {
2155         return this._minimumBoundary;
2156     },
2157
2158     /**
2159      * @return {number}
2160      */
2161     _value: function(item)
2162     {
2163         return 0;
2164     },
2165
2166     /**
2167      * @param {number} value
2168      * @param {number=} precision
2169      * @return {string}
2170      */
2171     formatTime: function(value, precision)
2172     {
2173         return value.toString();
2174     },
2175
2176     setDisplayWindow: function(clientWidth)
2177     {
2178         this._workingArea = clientWidth;
2179     },
2180
2181     /**
2182      * @return {number}
2183      */
2184     paddingLeft: function()
2185     {
2186         return 0;
2187     }
2188 }
2189
2190 /**
2191  * @constructor
2192  * @extends {WebInspector.NetworkBaseCalculator}
2193  */
2194 WebInspector.NetworkTimeCalculator = function(startAtZero)
2195 {
2196     WebInspector.NetworkBaseCalculator.call(this);
2197     this.startAtZero = startAtZero;
2198 }
2199
2200 WebInspector.NetworkTimeCalculator.prototype = {
2201     /**
2202      * @param {!WebInspector.NetworkRequest} request
2203      * @return {!{start: number, middle: number, end: number}}
2204      */
2205     computeBarGraphPercentages: function(request)
2206     {
2207         if (request.startTime !== -1)
2208             var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2209         else
2210             var start = 0;
2211
2212         if (request.responseReceivedTime !== -1)
2213             var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2214         else
2215             var middle = (this.startAtZero ? start : 100);
2216
2217         if (request.endTime !== -1)
2218             var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2219         else
2220             var end = (this.startAtZero ? middle : 100);
2221
2222         if (this.startAtZero) {
2223             end -= start;
2224             middle -= start;
2225             start = 0;
2226         }
2227
2228         return {start: start, middle: middle, end: end};
2229     },
2230
2231     /**
2232      * @return {number}
2233      */
2234     computePercentageFromEventTime: function(eventTime)
2235     {
2236         // This function computes a percentage in terms of the total loading time
2237         // of a specific event. If startAtZero is set, then this is useless, and we
2238         // want to return 0.
2239         if (eventTime !== -1 && !this.startAtZero)
2240             return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2241
2242         return 0;
2243     },
2244
2245     /**
2246      * @return {boolean}
2247      */
2248     updateBoundariesForEventTime: function(eventTime)
2249     {
2250         if (eventTime === -1 || this.startAtZero)
2251             return false;
2252
2253         if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2254             this._maximumBoundary = eventTime;
2255             return true;
2256         }
2257         return false;
2258     },
2259
2260     /**
2261      * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2262      */
2263     computeBarGraphLabels: function(request)
2264     {
2265         var rightLabel = "";
2266         if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2267             rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2268
2269         var hasLatency = request.latency > 0;
2270         if (hasLatency)
2271             var leftLabel = Number.secondsToString(request.latency);
2272         else
2273             var leftLabel = rightLabel;
2274
2275         if (request.timing)
2276             return {left: leftLabel, right: rightLabel};
2277
2278         if (hasLatency && rightLabel) {
2279             var total = Number.secondsToString(request.duration);
2280             var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
2281         } else if (hasLatency)
2282             var tooltip = WebInspector.UIString("%s latency", leftLabel);
2283         else if (rightLabel)
2284             var tooltip = WebInspector.UIString("%s download", rightLabel);
2285
2286         if (request.cached)
2287             tooltip = WebInspector.UIString("%s (from cache)", tooltip);
2288         return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2289     },
2290
2291     /**
2292      * @return {boolean}
2293      */
2294     updateBoundaries: function(request)
2295     {
2296         var didChange = false;
2297
2298         var lowerBound;
2299         if (this.startAtZero)
2300             lowerBound = 0;
2301         else
2302             lowerBound = this._lowerBound(request);
2303
2304         if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2305             this._minimumBoundary = lowerBound;
2306             didChange = true;
2307         }
2308
2309         var upperBound = this._upperBound(request);
2310         if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2311             this._maximumBoundary = upperBound;
2312             didChange = true;
2313         }
2314
2315         return didChange;
2316     },
2317
2318     /**
2319      * @return {string}
2320      */
2321     formatTime: function(value)
2322     {
2323         return Number.secondsToString(value);
2324     },
2325
2326     _lowerBound: function(request)
2327     {
2328         return 0;
2329     },
2330
2331     _upperBound: function(request)
2332     {
2333         return 0;
2334     },
2335
2336     __proto__: WebInspector.NetworkBaseCalculator.prototype
2337 }
2338
2339 /**
2340  * @constructor
2341  * @extends {WebInspector.NetworkTimeCalculator}
2342  */
2343 WebInspector.NetworkTransferTimeCalculator = function()
2344 {
2345     WebInspector.NetworkTimeCalculator.call(this, false);
2346 }
2347
2348 WebInspector.NetworkTransferTimeCalculator.prototype = {
2349     /**
2350      * @param {number} value
2351      * @return {string}
2352      */
2353     formatTime: function(value)
2354     {
2355         return Number.secondsToString(value - this.zeroTime());
2356     },
2357
2358     _lowerBound: function(request)
2359     {
2360         return request.startTime;
2361     },
2362
2363     _upperBound: function(request)
2364     {
2365         return request.endTime;
2366     },
2367
2368     __proto__: WebInspector.NetworkTimeCalculator.prototype
2369 }
2370
2371 /**
2372  * @constructor
2373  * @extends {WebInspector.NetworkTimeCalculator}
2374  */
2375 WebInspector.NetworkTransferDurationCalculator = function()
2376 {
2377     WebInspector.NetworkTimeCalculator.call(this, true);
2378 }
2379
2380 WebInspector.NetworkTransferDurationCalculator.prototype = {
2381     /**
2382      * @param {number} value
2383      * @return {string}
2384      */
2385     formatTime: function(value)
2386     {
2387         return Number.secondsToString(value);
2388     },
2389
2390     _upperBound: function(request)
2391     {
2392         return request.duration;
2393     },
2394
2395     __proto__: WebInspector.NetworkTimeCalculator.prototype
2396 }
2397
2398 /**
2399  * @constructor
2400  * @extends {WebInspector.DataGridNode}
2401  * @param {!WebInspector.NetworkLogView} parentView
2402  * @param {!WebInspector.NetworkRequest} request
2403  */
2404 WebInspector.NetworkDataGridNode = function(parentView, request)
2405 {
2406     WebInspector.DataGridNode.call(this, {});
2407     this._parentView = parentView;
2408     this._request = request;
2409     this._linkifier = new WebInspector.Linkifier();
2410 }
2411
2412 WebInspector.NetworkDataGridNode.prototype = {
2413     /** override */
2414     createCells: function()
2415     {
2416         this._nameCell = this._createDivInTD("name");
2417         this._methodCell = this._createDivInTD("method");
2418         this._statusCell = this._createDivInTD("status");
2419         this._schemeCell = this._createDivInTD("scheme");
2420         this._domainCell = this._createDivInTD("domain");
2421         this._remoteAddressCell = this._createDivInTD("remoteAddress");
2422         this._typeCell = this._createDivInTD("type");
2423         this._initiatorCell = this._createDivInTD("initiator");
2424         this._cookiesCell = this._createDivInTD("cookies");
2425         this._setCookiesCell = this._createDivInTD("setCookies");
2426         this._sizeCell = this._createDivInTD("size");
2427         this._timeCell = this._createDivInTD("time");
2428
2429         this._responseHeaderCells = {};
2430         var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2431         for (var i = 0; i < responseHeaderColumns.length; ++i)
2432             this._responseHeaderCells[responseHeaderColumns[i]] = this._createDivInTD(responseHeaderColumns[i]);
2433
2434         this._timelineCell = this._createDivInTD("timeline");
2435         this._createTimelineBar(this._timelineCell);
2436         this._nameCell.addEventListener("click", this._onClick.bind(this), false);
2437         this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2438     },
2439
2440     wasDetached: function()
2441     {
2442         this._linkifier.reset();
2443     },
2444
2445     /**
2446      * @return {boolean}
2447      */
2448     isFilteredOut: function()
2449     {
2450         return !!this._parentView._filteredOutRequests.get(this._request);
2451     },
2452
2453     _onClick: function()
2454     {
2455         if (!this._parentView._allowRequestSelection)
2456             this.select();
2457     },
2458
2459     select: function()
2460     {
2461         this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2462         WebInspector.DataGridNode.prototype.select.apply(this, arguments);
2463
2464         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2465             action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2466             url: this._request.url
2467         });
2468     },
2469
2470     _highlightMatchedSubstring: function(regexp)
2471     {
2472         var domChanges = [];
2473         var matchInfo = this._element.textContent.match(regexp);
2474         if (matchInfo)
2475             WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2476         return domChanges;
2477     },
2478
2479     _openInNewTab: function()
2480     {
2481         InspectorFrontendHost.openInNewTab(this._request.url);
2482     },
2483
2484     get selectable()
2485     {
2486         return this._parentView._allowRequestSelection && !this.isFilteredOut();
2487     },
2488
2489     _createDivInTD: function(columnIdentifier)
2490     {
2491         var td = this.createTD(columnIdentifier);
2492         var div = td.createChild("div");
2493         this._element.appendChild(td);
2494         return div;
2495     },
2496
2497     /**
2498      * @param {!Element} cell
2499      */
2500     _createTimelineBar: function(cell)
2501     {
2502         cell.className = "network-graph-side";
2503
2504         this._barAreaElement = document.createElement("div");
2505         //    this._barAreaElement.className = "network-graph-bar-area hidden";
2506         this._barAreaElement.className = "network-graph-bar-area";
2507         this._barAreaElement.request = this._request;
2508         cell.appendChild(this._barAreaElement);
2509
2510         this._barLeftElement = document.createElement("div");
2511         this._barLeftElement.className = "network-graph-bar waiting";
2512         this._barAreaElement.appendChild(this._barLeftElement);
2513
2514         this._barRightElement = document.createElement("div");
2515         this._barRightElement.className = "network-graph-bar";
2516         this._barAreaElement.appendChild(this._barRightElement);
2517
2518
2519         this._labelLeftElement = document.createElement("div");
2520         this._labelLeftElement.className = "network-graph-label waiting";
2521         this._barAreaElement.appendChild(this._labelLeftElement);
2522
2523         this._labelRightElement = document.createElement("div");
2524         this._labelRightElement.className = "network-graph-label";
2525         this._barAreaElement.appendChild(this._labelRightElement);
2526
2527         cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2528     },
2529
2530     refreshRequest: function()
2531     {
2532         this._refreshNameCell();
2533         this._refreshMethodCell();
2534         this._refreshStatusCell();
2535         this._refreshSchemeCell();
2536         this._refreshDomainCell();
2537         this._refreshRemoteAddressCell();
2538         this._refreshTypeCell();
2539         this._refreshInitiatorCell();
2540         this._refreshCookiesCell();
2541         this._refreshSetCookiesCell();
2542         this._refreshSizeCell();
2543         this._refreshTimeCell();
2544
2545         var responseHeaderColumns = WebInspector.NetworkLogView._responseHeaderColumns;
2546         for (var i = 0; i < responseHeaderColumns.length; ++i)
2547             this._refreshResponseHeaderCell(responseHeaderColumns[i]);
2548
2549         if (this._request.cached)
2550             this._timelineCell.classList.add("resource-cached");
2551
2552         this._element.classList.add("network-item");
2553         this._element.classList.toggle("network-error-row", this._isFailed());
2554         this._updateElementStyleClasses(this._element);
2555     },
2556
2557     /**
2558      * @return {boolean}
2559      */
2560     _isFailed: function()
2561     {
2562         return !!this._request.failed || (this._request.statusCode >= 400);
2563     },
2564
2565     /**
2566      * @param {!Element} element
2567      */
2568     _updateElementStyleClasses: function(element)
2569     {
2570         var typeClassName = "network-type-" + this._request.type.name();
2571         if (!element.classList.contains(typeClassName)) {
2572             element.removeMatchingStyleClasses("network-type-\\w+");
2573             element.classList.add(typeClassName);
2574         }
2575     },
2576
2577     _refreshResponseHeaderCell: function(headerName)
2578     {
2579         var cell = this._responseHeaderCells[headerName];
2580         var value = this._request.responseHeaderValue(headerName);
2581         cell.setTextAndTitle(value ? value : "");
2582     },
2583
2584     _refreshNameCell: function()
2585     {
2586         this._nameCell.removeChildren();
2587
2588         if (this._request.type === WebInspector.resourceTypes.Image) {
2589             var previewImage = document.createElement("img");
2590             previewImage.className = "image-network-icon-preview";
2591             this._request.populateImageSource(previewImage);
2592
2593             var iconElement = document.createElement("div");
2594             iconElement.className = "icon";
2595             iconElement.appendChild(previewImage);
2596         } else {
2597             var iconElement = document.createElement("img");
2598             iconElement.className = "icon";
2599         }
2600         this._nameCell.appendChild(iconElement);
2601         this._nameCell.appendChild(document.createTextNode(this._request.name()));
2602         this._appendSubtitle(this._nameCell, this._request.path());
2603         this._nameCell.title = this._request.url;
2604     },
2605
2606     _refreshMethodCell: function()
2607     {
2608         this._methodCell.setTextAndTitle(this._request.requestMethod);
2609     },
2610
2611     _refreshStatusCell: function()
2612     {
2613         this._statusCell.removeChildren();
2614         this._statusCell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2615
2616         if (this._request.failed && !this._request.canceled) {
2617             var failText = WebInspector.UIString("(failed)");
2618             if (this._request.localizedFailDescription) {
2619                 this._statusCell.appendChild(document.createTextNode(failText));
2620                 this._appendSubtitle(this._statusCell, this._request.localizedFailDescription);
2621                 this._statusCell.title = failText + " " + this._request.localizedFailDescription;
2622             } else
2623                 this._statusCell.setTextAndTitle(failText);
2624         } else if (this._request.statusCode) {
2625             this._statusCell.appendChild(document.createTextNode("" + this._request.statusCode));
2626             this._appendSubtitle(this._statusCell, this._request.statusText);
2627             this._statusCell.title = this._request.statusCode + " " + this._request.statusText;
2628         } else if (this._request.parsedURL.isDataURL()) {
2629             this._statusCell.setTextAndTitle(WebInspector.UIString("(data)"));
2630         } else if (this._request.isPingRequest()) {
2631             this._statusCell.setTextAndTitle(WebInspector.UIString("(ping)"));
2632         } else if (this._request.canceled) {
2633             this._statusCell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2634         } else if (this._request.finished) {
2635             this._statusCell.setTextAndTitle(WebInspector.UIString("Finished"));
2636         } else {
2637             this._statusCell.setTextAndTitle(WebInspector.UIString("(pending)"));
2638         }
2639     },
2640
2641     _refreshSchemeCell: function()
2642     {
2643         this._schemeCell.setTextAndTitle(this._request.scheme);
2644     },
2645
2646     _refreshDomainCell: function()
2647     {
2648         this._domainCell.setTextAndTitle(this._request.domain);
2649     },
2650
2651     _refreshRemoteAddressCell: function()
2652     {
2653         this._remoteAddressCell.setTextAndTitle(this._request.remoteAddress());
2654     },
2655
2656     _refreshTypeCell: function()
2657     {
2658         if (this._request.mimeType) {
2659             this._typeCell.classList.remove("network-dim-cell");
2660             this._typeCell.setTextAndTitle(this._request.mimeType);
2661         } else {
2662             this._typeCell.classList.toggle("network-dim-cell", !this._request.isPingRequest());
2663             this._typeCell.setTextAndTitle(this._request.requestContentType() || "");
2664         }
2665     },
2666
2667     _refreshInitiatorCell: function()
2668     {
2669         this._initiatorCell.removeChildren();
2670         this._initiatorCell.classList.remove("network-dim-cell");
2671         this._initiatorCell.classList.remove("network-script-initiated");
2672         delete this._initiatorCell.request;
2673
2674         var request = this._request;
2675         var initiator = request.initiatorInfo();
2676
2677         switch (initiator.type) {
2678         case WebInspector.NetworkRequest.InitiatorType.Parser:
2679             this._initiatorCell.title = initiator.url + ":" + initiator.lineNumber;
2680             this._initiatorCell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2681             this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Parser"));
2682             break;
2683
2684         case WebInspector.NetworkRequest.InitiatorType.Redirect:
2685             this._initiatorCell.title = initiator.url;
2686             console.assert(request.redirectSource);
2687             var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2688             this._initiatorCell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2689             this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Redirect"));
2690             break;
2691
2692         case WebInspector.NetworkRequest.InitiatorType.Script:
2693             var urlElement = this._linkifier.linkifyLocation(request.target(), initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2694             urlElement.title = "";
2695             this._initiatorCell.appendChild(urlElement);
2696             this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
2697             this._initiatorCell.classList.add("network-script-initiated");
2698             this._initiatorCell.request = request;
2699             break;
2700
2701         default:
2702             this._initiatorCell.title = "";
2703             this._initiatorCell.classList.add("network-dim-cell");
2704             this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
2705         }
2706     },
2707
2708     _refreshCookiesCell: function()
2709     {
2710         var requestCookies = this._request.requestCookies;
2711         this._cookiesCell.setTextAndTitle(requestCookies ? "" + requestCookies.length : "");
2712     },
2713
2714     _refreshSetCookiesCell: function()
2715     {
2716         var responseCookies = this._request.responseCookies;
2717         this._setCookiesCell.setTextAndTitle(responseCookies ? "" + responseCookies.length : "");
2718     },
2719
2720     _refreshSizeCell: function()
2721     {
2722         if (this._request.cached) {
2723             this._sizeCell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2724             this._sizeCell.classList.add("network-dim-cell");
2725         } else {
2726             var resourceSize = Number.bytesToString(this._request.resourceSize);
2727             var transferSize = Number.bytesToString(this._request.transferSize);
2728             this._sizeCell.setTextAndTitle(transferSize);
2729             this._sizeCell.classList.remove("network-dim-cell");
2730             this._appendSubtitle(this._sizeCell, resourceSize);
2731         }
2732     },
2733
2734     _refreshTimeCell: function()
2735     {
2736         if (this._request.duration > 0) {
2737             this._timeCell.classList.remove("network-dim-cell");
2738             this._timeCell.setTextAndTitle(Number.secondsToString(this._request.duration));
2739             this._appendSubtitle(this._timeCell, Number.secondsToString(this._request.latency));
2740         } else {
2741             this._timeCell.classList.add("network-dim-cell");
2742             this._timeCell.setTextAndTitle(WebInspector.UIString("Pending"));
2743         }
2744     },
2745
2746     _appendSubtitle: function(cellElement, subtitleText)
2747     {
2748         var subtitleElement = document.createElement("div");
2749         subtitleElement.className = "network-cell-subtitle";
2750         subtitleElement.textContent = subtitleText;
2751         cellElement.appendChild(subtitleElement);
2752     },
2753
2754     refreshGraph: function(calculator)
2755     {
2756         var percentages = calculator.computeBarGraphPercentages(this._request);
2757         this._percentages = percentages;
2758
2759         this._barAreaElement.classList.remove("hidden");
2760         this._updateElementStyleClasses(this._timelineCell);
2761
2762         this._barLeftElement.style.setProperty("left", percentages.start + "%");
2763         this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2764
2765         this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
2766         this._barRightElement.style.setProperty("left", percentages.middle + "%");
2767
2768         var labels = calculator.computeBarGraphLabels(this._request);
2769         this._labelLeftElement.textContent = labels.left;
2770         this._labelRightElement.textContent = labels.right;
2771
2772         var tooltip = (labels.tooltip || "");
2773         this._barLeftElement.title = tooltip;
2774         this._labelLeftElement.title = tooltip;
2775         this._labelRightElement.title = tooltip;
2776         this._barRightElement.title = tooltip;
2777     },
2778
2779     _refreshLabelPositions: function()
2780     {
2781         if (!this._percentages)
2782             return;
2783         this._labelLeftElement.style.removeProperty("left");
2784         this._labelLeftElement.style.removeProperty("right");
2785         this._labelLeftElement.classList.remove("before");
2786         this._labelLeftElement.classList.remove("hidden");
2787
2788         this._labelRightElement.style.removeProperty("left");
2789         this._labelRightElement.style.removeProperty("right");
2790         this._labelRightElement.classList.remove("after");
2791         this._labelRightElement.classList.remove("hidden");
2792
2793         const labelPadding = 10;
2794         const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2795         const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2796
2797         if (this._barLeftElement) {
2798             var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2799             var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2800         } else {
2801             var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2802             var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2803         }
2804
2805         const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2806         const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2807
2808         const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2809         const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2810         const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2811
2812         if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2813             var leftHidden = true;
2814
2815         if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2816             var rightHidden = true;
2817
2818         if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2819             // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2820             if (labelBefore && !labelAfter)
2821                 leftHidden = true;
2822             else if (labelAfter && !labelBefore)
2823                 rightHidden = true;
2824         }
2825
2826         if (labelBefore) {
2827             if (leftHidden)
2828                 this._labelLeftElement.classList.add("hidden");
2829             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2830             this._labelLeftElement.classList.add("before");
2831         } else {
2832             this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2833             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2834         }
2835
2836         if (labelAfter) {
2837             if (rightHidden)
2838                 this._labelRightElement.classList.add("hidden");
2839             this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2840             this._labelRightElement.classList.add("after");
2841         } else {
2842             this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2843             this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2844         }
2845     },
2846
2847     __proto__: WebInspector.DataGridNode.prototype
2848 }
2849
2850 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2851 {
2852     var aFileName = a._request.name();
2853     var bFileName = b._request.name();
2854     if (aFileName > bFileName)
2855         return 1;
2856     if (bFileName > aFileName)
2857         return -1;
2858     return 0;
2859 }
2860
2861 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2862 {
2863     var aRemoteAddress = a._request.remoteAddress();
2864     var bRemoteAddress = b._request.remoteAddress();
2865     if (aRemoteAddress > bRemoteAddress)
2866         return 1;
2867     if (bRemoteAddress > aRemoteAddress)
2868         return -1;
2869     return 0;
2870 }
2871
2872 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2873 {
2874     if (b._request.cached && !a._request.cached)
2875         return 1;
2876     if (a._request.cached && !b._request.cached)
2877         return -1;
2878
2879     return a._request.transferSize - b._request.transferSize;
2880 }
2881
2882 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
2883 {
2884     var aInitiator = a._request.initiatorInfo();
2885     var bInitiator = b._request.initiatorInfo();
2886
2887     if (aInitiator.type < bInitiator.type)
2888         return -1;
2889     if (aInitiator.type > bInitiator.type)
2890         return 1;
2891
2892     if (aInitiator.source < bInitiator.source)
2893         return -1;
2894     if (aInitiator.source > bInitiator.source)
2895         return 1;
2896
2897     if (aInitiator.lineNumber < bInitiator.lineNumber)
2898         return -1;
2899     if (aInitiator.lineNumber > bInitiator.lineNumber)
2900         return 1;
2901
2902     if (aInitiator.columnNumber < bInitiator.columnNumber)
2903         return -1;
2904     if (aInitiator.columnNumber > bInitiator.columnNumber)
2905         return 1;
2906
2907     return 0;
2908 }
2909
2910 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
2911 {
2912     var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
2913     var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
2914     return aScore - bScore;
2915 }
2916
2917 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
2918 {
2919     var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
2920     var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
2921     return aScore - bScore;
2922 }
2923
2924 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
2925 {
2926     var aValue = a._request[propertyName];
2927     var bValue = b._request[propertyName];
2928     if (aValue > bValue)
2929         return revert ? -1 : 1;
2930     if (bValue > aValue)
2931         return revert ? 1 : -1;
2932     return 0;
2933 }