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