Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / network / NetworkPanel.js
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
4  * Copyright (C) 2011 Google Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
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"), this._replayXHR.bind(this, request.requestId));
1205             contextMenu.appendSeparator();
1206         }
1207
1208         contextMenu.show();
1209     },
1210
1211     /**
1212      * @param {string} requestId
1213      */
1214     _replayXHR: function(requestId)
1215     {
1216         NetworkAgent.replayXHR(requestId);
1217     },
1218
1219     _harRequests: function()
1220     {
1221         var requests = this._nodesByRequestId.values().map(function(node) { return node.request(); });
1222         var httpRequests = requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter);
1223         httpRequests = httpRequests.filter(WebInspector.NetworkLogView.FinishedRequestsFilter);
1224         return httpRequests.filter(WebInspector.NetworkLogView.NonDevToolsRequestsFilter);
1225     },
1226
1227     _copyAll: function()
1228     {
1229         var harArchive = {
1230             log: (new WebInspector.HARLog(this._harRequests())).build()
1231         };
1232         InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
1233     },
1234
1235     /**
1236      * @param {!WebInspector.NetworkRequest} request
1237      */
1238     _copyLocation: function(request)
1239     {
1240         InspectorFrontendHost.copyText(request.url);
1241     },
1242
1243     /**
1244      * @param {!WebInspector.NetworkRequest} request
1245      */
1246     _copyRequestHeaders: function(request)
1247     {
1248         InspectorFrontendHost.copyText(request.requestHeadersText());
1249     },
1250
1251     /**
1252      * @param {!WebInspector.NetworkRequest} request
1253      */
1254     _copyResponse: function(request)
1255     {
1256         /**
1257          * @param {?string} content
1258          */
1259         function callback(content)
1260         {
1261             if (request.contentEncoded)
1262                 content = request.asDataURL();
1263             InspectorFrontendHost.copyText(content || "");
1264         }
1265         request.requestContent(callback);
1266     },
1267
1268     /**
1269      * @param {!WebInspector.NetworkRequest} request
1270      */
1271     _copyResponseHeaders: function(request)
1272     {
1273         InspectorFrontendHost.copyText(request.responseHeadersText);
1274     },
1275
1276     /**
1277      * @param {!WebInspector.NetworkRequest} request
1278      */
1279     _copyCurlCommand: function(request)
1280     {
1281         InspectorFrontendHost.copyText(this._generateCurlCommand(request));
1282     },
1283
1284     _exportAll: function()
1285     {
1286         var filename = WebInspector.targetManager.inspectedPageDomain() + ".har";
1287         var stream = new WebInspector.FileOutputStream();
1288         stream.open(filename, openCallback.bind(this));
1289
1290         /**
1291          * @param {boolean} accepted
1292          * @this {WebInspector.NetworkLogView}
1293          */
1294         function openCallback(accepted)
1295         {
1296             if (!accepted)
1297                 return;
1298             var progressIndicator = new WebInspector.ProgressIndicator();
1299             this._progressBarContainer.appendChild(progressIndicator.element);
1300             var harWriter = new WebInspector.HARWriter();
1301             harWriter.write(stream, this._harRequests(), progressIndicator);
1302         }
1303     },
1304
1305     _clearBrowserCache: function()
1306     {
1307         if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
1308             NetworkAgent.clearBrowserCache();
1309     },
1310
1311     _clearBrowserCookies: function()
1312     {
1313         if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
1314             NetworkAgent.clearBrowserCookies();
1315     },
1316
1317     /**
1318      * @param {!WebInspector.NetworkRequest} request
1319      * @return {boolean}
1320      */
1321     _matchRequest: function(request)
1322     {
1323         var re = this._searchRegExp;
1324         if (!re)
1325             return false;
1326         return re.test(request.name()) || re.test(request.path());
1327     },
1328
1329     _clearSearchMatchedList: function()
1330     {
1331         this._matchedRequestCount = -1;
1332         this._currentMatchedRequestNode = null;
1333         this._removeAllHighlights();
1334     },
1335
1336     _removeAllHighlights: function()
1337     {
1338         this._removeAllNodeHighlights();
1339         for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
1340             WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
1341         this._highlightedSubstringChanges = [];
1342     },
1343
1344     /**
1345      * @param {number} n
1346      * @param {boolean} reveal
1347      */
1348     _highlightNthMatchedRequestForSearch: function(n, reveal)
1349     {
1350         this._removeAllHighlights();
1351
1352         /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1353         var nodes = this._dataGrid.rootNode().children;
1354         var matchCount = 0;
1355         var node = null;
1356         for (var i = 0; i < nodes.length; ++i) {
1357             if (nodes[i]._isMatchingSearchQuery) {
1358                 if (matchCount === n) {
1359                     node = nodes[i];
1360                     break;
1361                 }
1362                 matchCount++;
1363             }
1364         }
1365         if (!node) {
1366             this._currentMatchedRequestNode = null;
1367             return;
1368         }
1369
1370         var request = node.request();
1371         var regExp = this._searchRegExp;
1372         var nameMatched = request.name().match(regExp);
1373         var pathMatched = request.path().match(regExp);
1374         if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
1375             this._toggleLargerRequests();
1376         if (reveal)
1377             WebInspector.Revealer.reveal(request);
1378         var highlightedSubstringChanges = node.highlightMatchedSubstring(regExp);
1379         this._highlightedSubstringChanges.push(highlightedSubstringChanges);
1380
1381         this._currentMatchedRequestNode = node;
1382         this._currentMatchedRequestIndex = n;
1383         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, n);
1384     },
1385
1386     /**
1387      * @param {string} query
1388      * @param {boolean} shouldJump
1389      * @param {boolean=} jumpBackwards
1390      */
1391     performSearch: function(query, shouldJump, jumpBackwards)
1392     {
1393         var currentMatchedRequestNode = this._currentMatchedRequestNode;
1394         this._clearSearchMatchedList();
1395         this._searchRegExp = createPlainTextSearchRegex(query, "i");
1396
1397         /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1398         var nodes = this._dataGrid.rootNode().children;
1399         for (var i = 0; i < nodes.length; ++i)
1400             nodes[i]._isMatchingSearchQuery = this._matchRequest(nodes[i].request());
1401         var newMatchedRequestIndex = this._updateMatchCountAndFindMatchIndex(currentMatchedRequestNode);
1402         if (!newMatchedRequestIndex && jumpBackwards)
1403             newMatchedRequestIndex = this._matchedRequestCount - 1;
1404         this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, shouldJump);
1405     },
1406
1407     /**
1408      * @param {?WebInspector.NetworkDataGridNode} node
1409      * @return {number}
1410      */
1411     _updateMatchCountAndFindMatchIndex: function(node)
1412     {
1413         /** @type {!Array.<!WebInspector.NetworkDataGridNode>} */
1414         var nodes = this._dataGrid.rootNode().children;
1415         var matchCount = 0;
1416         var matchIndex = 0;
1417         for (var i = 0; i < nodes.length; ++i) {
1418             if (!nodes[i]._isMatchingSearchQuery)
1419                 continue;
1420             if (node === nodes[i])
1421                 matchIndex = matchCount;
1422             matchCount++;
1423         }
1424         if (this._matchedRequestCount !== matchCount) {
1425             this._matchedRequestCount = matchCount;
1426             this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, matchCount);
1427         }
1428         return matchIndex;
1429     },
1430
1431     /**
1432      * @param {number} index
1433      * @return {number}
1434      */
1435     _normalizeSearchResultIndex: function(index)
1436     {
1437         return (index + this._matchedRequestCount) % this._matchedRequestCount;
1438     },
1439
1440     /**
1441      * @param {!WebInspector.NetworkDataGridNode} node
1442      * @return {boolean}
1443      */
1444     _applyFilter: function(node)
1445     {
1446         var request = node.request();
1447         if (!this._resourceTypeFilterUI.accept(request.type.name()))
1448             return false;
1449         if (this._dataURLFilterUI.checked() && request.parsedURL.isDataURL())
1450             return false;
1451         for (var i = 0; i < this._filters.length; ++i) {
1452             if (!this._filters[i](request))
1453                 return false;
1454         }
1455         return true;
1456     },
1457
1458     /**
1459      * @param {string} query
1460      */
1461     _parseFilterQuery: function(query)
1462     {
1463         var parsedQuery = this._suggestionBuilder.parseQuery(query);
1464         this._filters = parsedQuery.text.map(this._createTextFilter);
1465         for (var key in parsedQuery.filters) {
1466             var filterType = /** @type {!WebInspector.NetworkLogView.FilterType} */ (key);
1467             this._filters.push(this._createFilter(filterType, parsedQuery.filters[key]));
1468         }
1469     },
1470
1471     /**
1472      * @param {string} text
1473      * @return {!WebInspector.NetworkLogView.Filter}
1474      */
1475     _createTextFilter: function(text)
1476     {
1477         var regexp = new RegExp(text.escapeForRegExp(), "i");
1478         return WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp);
1479     },
1480
1481     /**
1482      * @param {!WebInspector.NetworkLogView.FilterType} type
1483      * @param {string} value
1484      * @return {!WebInspector.NetworkLogView.Filter}
1485      */
1486     _createFilter: function(type, value) {
1487         switch (type) {
1488         case WebInspector.NetworkLogView.FilterType.Domain:
1489             return WebInspector.NetworkLogView._requestDomainFilter.bind(null, value);
1490
1491         case WebInspector.NetworkLogView.FilterType.HasResponseHeader:
1492             return WebInspector.NetworkLogView._requestResponseHeaderFilter.bind(null, value);
1493
1494         case WebInspector.NetworkLogView.FilterType.Method:
1495             return WebInspector.NetworkLogView._requestMethodFilter.bind(null, value);
1496
1497         case WebInspector.NetworkLogView.FilterType.MimeType:
1498             return WebInspector.NetworkLogView._requestMimeTypeFilter.bind(null, value);
1499
1500         case WebInspector.NetworkLogView.FilterType.Scheme:
1501             return WebInspector.NetworkLogView._requestSchemeFilter.bind(null, value);
1502
1503         case WebInspector.NetworkLogView.FilterType.SetCookieDomain:
1504             return WebInspector.NetworkLogView._requestSetCookieDomainFilter.bind(null, value);
1505
1506         case WebInspector.NetworkLogView.FilterType.SetCookieName:
1507             return WebInspector.NetworkLogView._requestSetCookieNameFilter.bind(null, value);
1508
1509         case WebInspector.NetworkLogView.FilterType.SetCookieValue:
1510             return WebInspector.NetworkLogView._requestSetCookieValueFilter.bind(null, value);
1511
1512         case WebInspector.NetworkLogView.FilterType.StatusCode:
1513             return WebInspector.NetworkLogView._statusCodeFilter.bind(null, value);
1514         }
1515         return this._createTextFilter(type + ":" + value);
1516     },
1517
1518     _filterRequests: function()
1519     {
1520         this._removeAllHighlights();
1521         this._invalidateAllItems();
1522         this.refresh();
1523     },
1524
1525     jumpToPreviousSearchResult: function()
1526     {
1527         if (!this._matchedRequestCount)
1528             return;
1529         var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex - 1);
1530         this._highlightNthMatchedRequestForSearch(index, true);
1531     },
1532
1533     jumpToNextSearchResult: function()
1534     {
1535         if (!this._matchedRequestCount)
1536             return;
1537         var index = this._normalizeSearchResultIndex(this._currentMatchedRequestIndex + 1);
1538         this._highlightNthMatchedRequestForSearch(index, true);
1539     },
1540
1541     searchCanceled: function()
1542     {
1543         delete this._searchRegExp;
1544         this._clearSearchMatchedList();
1545         this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
1546     },
1547
1548     /**
1549      * @param {!WebInspector.NetworkRequest} request
1550      */
1551     revealAndHighlightRequest: function(request)
1552     {
1553         this._removeAllNodeHighlights();
1554
1555         var node = this._nodesByRequestId.get(request.requestId);
1556         if (node) {
1557             node.reveal();
1558             this._highlightNode(node);
1559         }
1560     },
1561
1562     _removeAllNodeHighlights: function()
1563     {
1564         if (this._highlightedNode) {
1565             this._highlightedNode.element().classList.remove("highlighted-row");
1566             delete this._highlightedNode;
1567         }
1568     },
1569
1570     /**
1571      * @param {!WebInspector.NetworkDataGridNode} node
1572      */
1573     _highlightNode: function(node)
1574     {
1575         WebInspector.runCSSAnimationOnce(node.element(), "highlighted-row");
1576         this._highlightedNode = node;
1577     },
1578
1579     /**
1580      * @param {!WebInspector.NetworkRequest} request
1581      * @return {string}
1582      */
1583     _generateCurlCommand: function(request)
1584     {
1585         var command = ["curl"];
1586         // These headers are derived from URL (except "version") and would be added by cURL anyway.
1587         var ignoredHeaders = {"host": 1, "method": 1, "path": 1, "scheme": 1, "version": 1};
1588
1589         function escapeStringWin(str)
1590         {
1591             /* Replace quote by double quote (but not by \") because it is
1592                recognized by both cmd.exe and MS Crt arguments parser.
1593
1594                Replace % by "%" because it could be expanded to an environment
1595                variable value. So %% becomes "%""%". Even if an env variable ""
1596                (2 doublequotes) is declared, the cmd.exe will not
1597                substitute it with its value.
1598
1599                Replace each backslash with double backslash to make sure
1600                MS Crt arguments parser won't collapse them.
1601
1602                Replace new line outside of quotes since cmd.exe doesn't let
1603                to do it inside.
1604             */
1605             return "\"" + str.replace(/"/g, "\"\"")
1606                              .replace(/%/g, "\"%\"")
1607                              .replace(/\\/g, "\\\\")
1608                              .replace(/[\r\n]+/g, "\"^$&\"") + "\"";
1609         }
1610
1611         function escapeStringPosix(str)
1612         {
1613             function escapeCharacter(x)
1614             {
1615                 var code = x.charCodeAt(0);
1616                 if (code < 256) {
1617                     // Add leading zero when needed to not care about the next character.
1618                     return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
1619                  }
1620                  code = code.toString(16);
1621                  return "\\u" + ("0000" + code).substr(code.length, 4);
1622              }
1623
1624             if (/[^\x20-\x7E]|\'/.test(str)) {
1625                 // Use ANSI-C quoting syntax.
1626                 return "$\'" + str.replace(/\\/g, "\\\\")
1627                                   .replace(/\'/g, "\\\'")
1628                                   .replace(/\n/g, "\\n")
1629                                   .replace(/\r/g, "\\r")
1630                                   .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
1631             } else {
1632                 // Use single quote syntax.
1633                 return "'" + str + "'";
1634             }
1635         }
1636
1637         // cURL command expected to run on the same platform that DevTools run
1638         // (it may be different from the inspected page platform).
1639         var escapeString = WebInspector.isWin() ? escapeStringWin : escapeStringPosix;
1640
1641         command.push(escapeString(request.url).replace(/[[{}\]]/g, "\\$&"));
1642
1643         var inferredMethod = "GET";
1644         var data = [];
1645         var requestContentType = request.requestContentType();
1646         if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
1647            data.push("--data");
1648            data.push(escapeString(request.requestFormData));
1649            ignoredHeaders["content-length"] = true;
1650            inferredMethod = "POST";
1651         } else if (request.requestFormData) {
1652            data.push("--data-binary");
1653            data.push(escapeString(request.requestFormData));
1654            ignoredHeaders["content-length"] = true;
1655            inferredMethod = "POST";
1656         }
1657
1658         if (request.requestMethod !== inferredMethod) {
1659             command.push("-X");
1660             command.push(request.requestMethod);
1661         }
1662
1663         var requestHeaders = request.requestHeaders();
1664         for (var i = 0; i < requestHeaders.length; i++) {
1665             var header = requestHeaders[i];
1666             var name = header.name.replace(/^:/, ""); // Translate SPDY v3 headers to HTTP headers.
1667             if (name.toLowerCase() in ignoredHeaders)
1668                 continue;
1669             command.push("-H");
1670             command.push(escapeString(name + ": " + header.value));
1671         }
1672         command = command.concat(data);
1673         command.push("--compressed");
1674         return command.join(" ");
1675     },
1676
1677     __proto__: WebInspector.VBox.prototype
1678 }
1679
1680 /** @typedef {function(!WebInspector.NetworkRequest): boolean} */
1681 WebInspector.NetworkLogView.Filter;
1682
1683 /**
1684  * @param {!RegExp} regex
1685  * @param {!WebInspector.NetworkRequest} request
1686  * @return {boolean}
1687  */
1688 WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request)
1689 {
1690     return regex.test(request.name()) || regex.test(request.path());
1691 }
1692
1693 /**
1694  * @param {string} value
1695  * @param {!WebInspector.NetworkRequest} request
1696  * @return {boolean}
1697  */
1698 WebInspector.NetworkLogView._requestDomainFilter = function(value, request)
1699 {
1700     return request.domain === value;
1701 }
1702
1703 /**
1704  * @param {string} value
1705  * @param {!WebInspector.NetworkRequest} request
1706  * @return {boolean}
1707  */
1708 WebInspector.NetworkLogView._requestResponseHeaderFilter = function(value, request)
1709 {
1710     return request.responseHeaderValue(value) !== undefined;
1711 }
1712
1713 /**
1714  * @param {string} value
1715  * @param {!WebInspector.NetworkRequest} request
1716  * @return {boolean}
1717  */
1718 WebInspector.NetworkLogView._requestMethodFilter = function(value, request)
1719 {
1720     return request.requestMethod === value;
1721 }
1722
1723 /**
1724  * @param {string} value
1725  * @param {!WebInspector.NetworkRequest} request
1726  * @return {boolean}
1727  */
1728 WebInspector.NetworkLogView._requestMimeTypeFilter = function(value, request)
1729 {
1730     return request.mimeType === value;
1731 }
1732
1733 /**
1734  * @param {string} value
1735  * @param {!WebInspector.NetworkRequest} request
1736  * @return {boolean}
1737  */
1738 WebInspector.NetworkLogView._requestSchemeFilter = function(value, request)
1739 {
1740     return request.scheme === value;
1741 }
1742
1743 /**
1744  * @param {string} value
1745  * @param {!WebInspector.NetworkRequest} request
1746  * @return {boolean}
1747  */
1748 WebInspector.NetworkLogView._requestSetCookieDomainFilter = function(value, request)
1749 {
1750     var cookies = request.responseCookies;
1751     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1752         if (cookies[i].domain() === value)
1753             return false;
1754     }
1755     return false;
1756 }
1757
1758 /**
1759  * @param {string} value
1760  * @param {!WebInspector.NetworkRequest} request
1761  * @return {boolean}
1762  */
1763 WebInspector.NetworkLogView._requestSetCookieNameFilter = function(value, request)
1764 {
1765     var cookies = request.responseCookies;
1766     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1767         if (cookies[i].name() === value)
1768             return false;
1769     }
1770     return false;
1771 }
1772
1773 /**
1774  * @param {string} value
1775  * @param {!WebInspector.NetworkRequest} request
1776  * @return {boolean}
1777  */
1778 WebInspector.NetworkLogView._requestSetCookieValueFilter = function(value, request)
1779 {
1780     var cookies = request.responseCookies;
1781     for (var i = 0, l = cookies ? cookies.length : 0; i < l; ++i) {
1782         if (cookies[i].value() === value)
1783             return false;
1784     }
1785     return false;
1786 }
1787
1788 /**
1789  * @param {string} value
1790  * @param {!WebInspector.NetworkRequest} request
1791  * @return {boolean}
1792  */
1793 WebInspector.NetworkLogView._statusCodeFilter = function(value, request)
1794 {
1795     return ("" + request.statusCode) === value;
1796 }
1797
1798 /**
1799  * @param {!WebInspector.NetworkRequest} request
1800  * @return {boolean}
1801  */
1802 WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
1803 {
1804     return request.parsedURL.isValid && (request.scheme in WebInspector.NetworkLogView.HTTPSchemas);
1805 }
1806
1807 /**
1808  * @param {!WebInspector.NetworkRequest} request
1809  * @return {boolean}
1810  */
1811 WebInspector.NetworkLogView.NonDevToolsRequestsFilter = function(request)
1812 {
1813     return !WebInspector.NetworkManager.hasDevToolsRequestHeader(request);
1814 }
1815
1816 /**
1817  * @param {!WebInspector.NetworkRequest} request
1818  * @return {boolean}
1819  */
1820 WebInspector.NetworkLogView.FinishedRequestsFilter = function(request)
1821 {
1822     return request.finished;
1823 }
1824
1825 WebInspector.NetworkLogView.EventTypes = {
1826     ViewCleared: "ViewCleared",
1827     RowSizeChanged: "RowSizeChanged",
1828     RequestSelected: "RequestSelected",
1829     SearchCountUpdated: "SearchCountUpdated",
1830     SearchIndexUpdated: "SearchIndexUpdated"
1831 };
1832
1833 /**
1834  * @constructor
1835  * @implements {WebInspector.ContextMenu.Provider}
1836  * @implements {WebInspector.Searchable}
1837  * @extends {WebInspector.Panel}
1838  */
1839 WebInspector.NetworkPanel = function()
1840 {
1841     WebInspector.Panel.call(this, "network");
1842     this.registerRequiredCSS("networkPanel.css");
1843
1844     this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
1845     this._filterBar = new WebInspector.FilterBar();
1846     this._filtersContainer = this.element.createChild("div", "network-filters-header hidden");
1847     this._filtersContainer.appendChild(this._filterBar.filtersElement());
1848     this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
1849     this._filterBar.setName("networkPanel");
1850
1851     this._searchableView = new WebInspector.SearchableView(this);
1852     this._searchableView.show(this.element);
1853     var contentsElement = this._searchableView.element;
1854
1855     this._splitView = new WebInspector.SplitView(true, false, "networkPanelSplitViewState");
1856     this._splitView.show(contentsElement);
1857     this._splitView.hideMain();
1858
1859     var defaultColumnsVisibility = WebInspector.NetworkLogView.defaultColumnsVisibility;
1860     var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
1861     var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
1862     var columnsVisibility = {};
1863     for (var columnId in defaultColumnsVisibility)
1864         columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
1865     networkLogColumnsVisibilitySetting.set(columnsVisibility);
1866
1867     /** @type {!WebInspector.NetworkLogView} */
1868     this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, networkLogColumnsVisibilitySetting);
1869     this._networkLogView.show(this._splitView.sidebarElement());
1870
1871     var viewsContainerView = new WebInspector.VBox();
1872     this._viewsContainerElement = viewsContainerView.element;
1873     this._viewsContainerElement.id = "network-views";
1874     if (!this._networkLogView.usesLargeRows())
1875         this._viewsContainerElement.classList.add("small");
1876     viewsContainerView.show(this._splitView.mainElement());
1877
1878     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
1879     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
1880     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
1881     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
1882     this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
1883
1884     this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
1885     this._closeButtonElement.id = "network-close-button";
1886     this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
1887     this._viewsContainerElement.appendChild(this._closeButtonElement);
1888
1889     var statusBarItems = this._networkLogView.statusBarItems();
1890     for (var i = 0; i < statusBarItems.length; ++i)
1891         this._panelStatusBarElement.appendChild(statusBarItems[i]);
1892
1893     /**
1894      * @this {WebInspector.NetworkPanel}
1895      * @return {?WebInspector.SourceFrame}
1896      */
1897     function sourceFrameGetter()
1898     {
1899         return this._networkItemView.currentSourceFrame();
1900     }
1901     WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
1902 }
1903
1904 WebInspector.NetworkPanel.prototype = {
1905     /**
1906      * @param {!WebInspector.Event} event
1907      */
1908     _onFiltersToggled: function(event)
1909     {
1910         var toggled = /** @type {boolean} */ (event.data);
1911         this._filtersContainer.classList.toggle("hidden", !toggled);
1912         this.element.classList.toggle("filters-toggled", toggled);
1913         this.doResize();
1914     },
1915
1916     /**
1917      * @return {!Array.<!Element>}
1918      */
1919     elementsToRestoreScrollPositionsFor: function()
1920     {
1921         return this._networkLogView.elementsToRestoreScrollPositionsFor();
1922     },
1923
1924     /**
1925      * @return {!WebInspector.SearchableView}
1926      */
1927     searchableView: function()
1928     {
1929         return this._searchableView;
1930     },
1931
1932     /**
1933      * @param {!KeyboardEvent} event
1934      */
1935     handleShortcut: function(event)
1936     {
1937         if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
1938             this._toggleGridMode();
1939             event.handled = true;
1940             return;
1941         }
1942
1943         WebInspector.Panel.prototype.handleShortcut.call(this, event);
1944     },
1945
1946     wasShown: function()
1947     {
1948         WebInspector.Panel.prototype.wasShown.call(this);
1949     },
1950
1951     /**
1952      * @param {!WebInspector.NetworkRequest} request
1953      */
1954     revealAndHighlightRequest: function(request)
1955     {
1956         this._toggleGridMode();
1957         if (request)
1958             this._networkLogView.revealAndHighlightRequest(request);
1959     },
1960
1961     /**
1962      * @param {!WebInspector.Event} event
1963      */
1964     _onViewCleared: function(event)
1965     {
1966         this._closeVisibleRequest();
1967         this._toggleGridMode();
1968         this._viewsContainerElement.removeChildren();
1969         this._viewsContainerElement.appendChild(this._closeButtonElement);
1970     },
1971
1972     /**
1973      * @param {!WebInspector.Event} event
1974      */
1975     _onRowSizeChanged: function(event)
1976     {
1977         this._viewsContainerElement.classList.toggle("small", !event.data.largeRows);
1978     },
1979
1980     /**
1981      * @param {!WebInspector.Event} event
1982      */
1983     _onSearchCountUpdated: function(event)
1984     {
1985         var count = /** @type {number} */ (event.data);
1986         this._searchableView.updateSearchMatchesCount(count);
1987     },
1988
1989     /**
1990      * @param {!WebInspector.Event} event
1991      */
1992     _onSearchIndexUpdated: function(event)
1993     {
1994         var index = /** @type {number} */ (event.data);
1995         this._searchableView.updateCurrentMatchIndex(index);
1996     },
1997
1998     /**
1999      * @param {!WebInspector.Event} event
2000      */
2001     _onRequestSelected: function(event)
2002     {
2003         var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
2004         this._showRequest(request);
2005     },
2006
2007     /**
2008      * @param {?WebInspector.NetworkRequest} request
2009      */
2010     _showRequest: function(request)
2011     {
2012         if (!request)
2013             return;
2014
2015         this._toggleViewingRequestMode();
2016
2017         if (this._networkItemView) {
2018             this._networkItemView.detach();
2019             delete this._networkItemView;
2020         }
2021
2022         var view = new WebInspector.NetworkItemView(request);
2023         view.show(this._viewsContainerElement);
2024         this._networkItemView = view;
2025     },
2026
2027     _closeVisibleRequest: function()
2028     {
2029         this.element.classList.remove("viewing-resource");
2030
2031         if (this._networkItemView) {
2032             this._networkItemView.detach();
2033             delete this._networkItemView;
2034         }
2035     },
2036
2037     _toggleGridMode: function()
2038     {
2039         if (this._viewingRequestMode) {
2040             this._viewingRequestMode = false;
2041             this.element.classList.remove("viewing-resource");
2042             this._splitView.hideMain();
2043         }
2044
2045         this._networkLogView.switchViewMode(true);
2046         this._networkLogView.setAllowPopover(true);
2047         this._networkLogView._allowRequestSelection = false;
2048     },
2049
2050     _toggleViewingRequestMode: function()
2051     {
2052         if (this._viewingRequestMode)
2053             return;
2054         this._viewingRequestMode = true;
2055
2056         this.element.classList.add("viewing-resource");
2057         this._splitView.showBoth();
2058         this._networkLogView.setAllowPopover(false);
2059         this._networkLogView._allowRequestSelection = true;
2060         this._networkLogView.switchViewMode(false);
2061     },
2062
2063     /**
2064      * @param {string} query
2065      * @param {boolean} shouldJump
2066      * @param {boolean=} jumpBackwards
2067      */
2068     performSearch: function(query, shouldJump, jumpBackwards)
2069     {
2070         this._networkLogView.performSearch(query, shouldJump, jumpBackwards);
2071     },
2072
2073     jumpToPreviousSearchResult: function()
2074     {
2075         this._networkLogView.jumpToPreviousSearchResult();
2076     },
2077
2078     jumpToNextSearchResult: function()
2079     {
2080         this._networkLogView.jumpToNextSearchResult();
2081     },
2082
2083     searchCanceled: function()
2084     {
2085         this._networkLogView.searchCanceled();
2086     },
2087
2088     /**
2089      * @param {!Event} event
2090      * @param {!WebInspector.ContextMenu} contextMenu
2091      * @param {!Object} target
2092      * @this {WebInspector.NetworkPanel}
2093      */
2094     appendApplicableItems: function(event, contextMenu, target)
2095     {
2096         /**
2097          * @this {WebInspector.NetworkPanel}
2098          */
2099         function reveal(request)
2100         {
2101             WebInspector.inspectorView.setCurrentPanel(this);
2102             this.revealAndHighlightRequest(request);
2103         }
2104
2105         /**
2106          * @this {WebInspector.NetworkPanel}
2107          */
2108         function appendRevealItem(request)
2109         {
2110             var revealText = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel");
2111             contextMenu.appendItem(revealText, reveal.bind(this, request));
2112         }
2113
2114         if (target instanceof WebInspector.Resource) {
2115             var resource = /** @type {!WebInspector.Resource} */ (target);
2116             if (resource.request)
2117                 appendRevealItem.call(this, resource.request);
2118             return;
2119         }
2120         if (target instanceof WebInspector.UISourceCode) {
2121             var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
2122             var resource = WebInspector.resourceForURL(uiSourceCode.url);
2123             if (resource && resource.request)
2124                 appendRevealItem.call(this, resource.request);
2125             return;
2126         }
2127
2128         if (!(target instanceof WebInspector.NetworkRequest))
2129             return;
2130         var request = /** @type {!WebInspector.NetworkRequest} */ (target);
2131         if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
2132             return;
2133
2134         appendRevealItem.call(this, request);
2135     },
2136
2137     __proto__: WebInspector.Panel.prototype
2138 }
2139
2140 /**
2141  * @constructor
2142  * @implements {WebInspector.ContextMenu.Provider}
2143  */
2144 WebInspector.NetworkPanel.ContextMenuProvider = function()
2145 {
2146 }
2147
2148 WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
2149     /**
2150      * @param {!Event} event
2151      * @param {!WebInspector.ContextMenu} contextMenu
2152      * @param {!Object} target
2153      */
2154     appendApplicableItems: function(event, contextMenu, target)
2155     {
2156         WebInspector.inspectorView.panel("network").appendApplicableItems(event, contextMenu, target);
2157     }
2158 }
2159
2160 /**
2161  * @constructor
2162  * @implements {WebInspector.Revealer}
2163  */
2164 WebInspector.NetworkPanel.RequestRevealer = function()
2165 {
2166 }
2167
2168 WebInspector.NetworkPanel.RequestRevealer.prototype = {
2169     /**
2170      * @param {!Object} request
2171      * @param {number=} lineNumber
2172      */
2173     reveal: function(request, lineNumber)
2174     {
2175         if (request instanceof WebInspector.NetworkRequest) {
2176             var panel = /** @type {?WebInspector.NetworkPanel} */ (WebInspector.inspectorView.showPanel("network"));
2177             if (panel)
2178                 panel.revealAndHighlightRequest(request);
2179         }
2180     }
2181 }
2182
2183 /**
2184  * @constructor
2185  * @implements {WebInspector.TimelineGrid.Calculator}
2186  */
2187 WebInspector.NetworkTimeCalculator = function(startAtZero)
2188 {
2189     this.startAtZero = startAtZero;
2190 }
2191
2192 /** @type {!WebInspector.UIStringFormat} */
2193 WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat = new WebInspector.UIStringFormat("%s latency, %s download (%s total)");
2194
2195 /** @type {!WebInspector.UIStringFormat} */
2196 WebInspector.NetworkTimeCalculator._latencyFormat = new WebInspector.UIStringFormat("%s latency");
2197
2198 /** @type {!WebInspector.UIStringFormat} */
2199 WebInspector.NetworkTimeCalculator._downloadFormat = new WebInspector.UIStringFormat("%s download");
2200
2201 /** @type {!WebInspector.UIStringFormat} */
2202 WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat = new WebInspector.UIStringFormat("%s (from ServiceWorker)");
2203
2204 /** @type {!WebInspector.UIStringFormat} */
2205 WebInspector.NetworkTimeCalculator._fromCacheFormat = new WebInspector.UIStringFormat("%s (from cache)");
2206
2207 WebInspector.NetworkTimeCalculator.prototype = {
2208     /**
2209      * @override
2210      * @return {number}
2211      */
2212     paddingLeft: function()
2213     {
2214         return 0;
2215     },
2216
2217     /**
2218      * @override
2219      * @param {number} time
2220      * @return {number}
2221      */
2222     computePosition: function(time)
2223     {
2224         return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
2225     },
2226
2227     /**
2228      * @override
2229      * @param {number} value
2230      * @param {number=} precision
2231      * @return {string}
2232      */
2233     formatTime: function(value, precision)
2234     {
2235         return Number.secondsToString(value);
2236     },
2237
2238     /**
2239      * @override
2240      * @return {number}
2241      */
2242     minimumBoundary: function()
2243     {
2244         return this._minimumBoundary;
2245     },
2246
2247     /**
2248      * @override
2249      * @return {number}
2250      */
2251     zeroTime: function()
2252     {
2253         return this._minimumBoundary;
2254     },
2255
2256     /**
2257      * @override
2258      * @return {number}
2259      */
2260     maximumBoundary: function()
2261     {
2262         return this._maximumBoundary;
2263     },
2264
2265     /**
2266      * @override
2267      * @return {number}
2268      */
2269     boundarySpan: function()
2270     {
2271         return this._maximumBoundary - this._minimumBoundary;
2272     },
2273
2274     reset: function()
2275     {
2276         delete this._minimumBoundary;
2277         delete this._maximumBoundary;
2278     },
2279
2280     /**
2281      * @return {number}
2282      */
2283     _value: function(item)
2284     {
2285         return 0;
2286     },
2287
2288     /**
2289      * @param {number} clientWidth
2290      */
2291     setDisplayWindow: function(clientWidth)
2292     {
2293         this._workingArea = clientWidth;
2294     },
2295
2296     /**
2297      * @param {!WebInspector.NetworkRequest} request
2298      * @return {!{start: number, middle: number, end: number}}
2299      */
2300     computeBarGraphPercentages: function(request)
2301     {
2302         if (request.startTime !== -1)
2303             var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2304         else
2305             var start = 0;
2306
2307         if (request.responseReceivedTime !== -1)
2308             var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2309         else
2310             var middle = (this.startAtZero ? start : 100);
2311
2312         if (request.endTime !== -1)
2313             var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2314         else
2315             var end = (this.startAtZero ? middle : 100);
2316
2317         if (this.startAtZero) {
2318             end -= start;
2319             middle -= start;
2320             start = 0;
2321         }
2322
2323         return {start: start, middle: middle, end: end};
2324     },
2325
2326     /**
2327      * @param {number} eventTime
2328      * @return {number}
2329      */
2330     computePercentageFromEventTime: function(eventTime)
2331     {
2332         // This function computes a percentage in terms of the total loading time
2333         // of a specific event. If startAtZero is set, then this is useless, and we
2334         // want to return 0.
2335         if (eventTime !== -1 && !this.startAtZero)
2336             return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
2337
2338         return 0;
2339     },
2340
2341     /**
2342      * @param {number} eventTime
2343      * @return {boolean}
2344      */
2345     updateBoundariesForEventTime: function(eventTime)
2346     {
2347         if (eventTime === -1 || this.startAtZero)
2348             return false;
2349
2350         if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
2351             this._maximumBoundary = eventTime;
2352             return true;
2353         }
2354         return false;
2355     },
2356
2357     /**
2358      * @param {!WebInspector.NetworkRequest} request
2359      * @return {!{left: string, right: string, tooltip: (string|undefined)}}
2360      */
2361     computeBarGraphLabels: function(request)
2362     {
2363         var rightLabel = "";
2364         if (request.responseReceivedTime !== -1 && request.endTime !== -1)
2365             rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime);
2366
2367         var hasLatency = request.latency > 0;
2368         if (hasLatency)
2369             var leftLabel = Number.secondsToString(request.latency);
2370         else
2371             var leftLabel = rightLabel;
2372
2373         if (request.timing)
2374             return {left: leftLabel, right: rightLabel};
2375
2376         if (hasLatency && rightLabel) {
2377             var total = Number.secondsToString(request.duration);
2378             var tooltip = WebInspector.NetworkTimeCalculator._latencyDownloadTotalFormat.format(leftLabel, rightLabel, total);
2379         } else if (hasLatency)
2380             var tooltip = WebInspector.NetworkTimeCalculator._latencyFormat.format(leftLabel);
2381         else if (rightLabel)
2382             var tooltip = WebInspector.NetworkTimeCalculator._downloadFormat.format(rightLabel);
2383
2384         if (request.fetchedViaServiceWorker)
2385             tooltip = WebInspector.NetworkTimeCalculator._fromServiceWorkerFormat.format(tooltip);
2386         else if (request.cached)
2387             tooltip = WebInspector.NetworkTimeCalculator._fromCacheFormat.format(tooltip);
2388         return {left: leftLabel, right: rightLabel, tooltip: tooltip};
2389     },
2390
2391     /**
2392      * @param {!WebInspector.NetworkRequest} request
2393      * @return {boolean}
2394      */
2395     updateBoundaries: function(request)
2396     {
2397         var didChange = false;
2398
2399         var lowerBound;
2400         if (this.startAtZero)
2401             lowerBound = 0;
2402         else
2403             lowerBound = this._lowerBound(request);
2404
2405         if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
2406             this._minimumBoundary = lowerBound;
2407             didChange = true;
2408         }
2409
2410         var upperBound = this._upperBound(request);
2411         if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
2412             this._maximumBoundary = upperBound;
2413             didChange = true;
2414         }
2415
2416         return didChange;
2417     },
2418
2419     /**
2420      * @param {!WebInspector.NetworkRequest} request
2421      * @return {number}
2422      */
2423     _lowerBound: function(request)
2424     {
2425         return 0;
2426     },
2427
2428     /**
2429      * @param {!WebInspector.NetworkRequest} request
2430      * @return {number}
2431      */
2432     _upperBound: function(request)
2433     {
2434         return 0;
2435     }
2436 }
2437
2438 /**
2439  * @constructor
2440  * @extends {WebInspector.NetworkTimeCalculator}
2441  */
2442 WebInspector.NetworkTransferTimeCalculator = function()
2443 {
2444     WebInspector.NetworkTimeCalculator.call(this, false);
2445 }
2446
2447 WebInspector.NetworkTransferTimeCalculator.prototype = {
2448     /**
2449      * @override
2450      * @param {number} value
2451      * @param {number=} precision
2452      * @return {string}
2453      */
2454     formatTime: function(value, precision)
2455     {
2456         return Number.secondsToString(value - this.zeroTime());
2457     },
2458
2459     /**
2460      * @override
2461      * @param {!WebInspector.NetworkRequest} request
2462      * @return {number}
2463      */
2464     _lowerBound: function(request)
2465     {
2466         return request.startTime;
2467     },
2468
2469     /**
2470      * @override
2471      * @param {!WebInspector.NetworkRequest} request
2472      * @return {number}
2473      */
2474     _upperBound: function(request)
2475     {
2476         return request.endTime;
2477     },
2478
2479     __proto__: WebInspector.NetworkTimeCalculator.prototype
2480 }
2481
2482 /**
2483  * @constructor
2484  * @extends {WebInspector.NetworkTimeCalculator}
2485  */
2486 WebInspector.NetworkTransferDurationCalculator = function()
2487 {
2488     WebInspector.NetworkTimeCalculator.call(this, true);
2489 }
2490
2491 WebInspector.NetworkTransferDurationCalculator.prototype = {
2492     /**
2493      * @override
2494      * @param {number} value
2495      * @param {number=} precision
2496      * @return {string}
2497      */
2498     formatTime: function(value, precision)
2499     {
2500         return Number.secondsToString(value);
2501     },
2502
2503     /**
2504      * @override
2505      * @param {!WebInspector.NetworkRequest} request
2506      * @return {number}
2507      */
2508     _upperBound: function(request)
2509     {
2510         return request.duration;
2511     },
2512
2513     __proto__: WebInspector.NetworkTimeCalculator.prototype
2514 }
2515
2516 /**
2517  * @constructor
2518  * @extends {WebInspector.SortableDataGridNode}
2519  * @param {!WebInspector.NetworkLogView} parentView
2520  * @param {!WebInspector.NetworkRequest} request
2521  */
2522 WebInspector.NetworkDataGridNode = function(parentView, request)
2523 {
2524     WebInspector.SortableDataGridNode.call(this, {});
2525     this._parentView = parentView;
2526     this._request = request;
2527     this._linkifier = new WebInspector.Linkifier();
2528     this._isFilteredOut = true;
2529     this._isMatchingSearchQuery = false;
2530     this._staleGraph = true;
2531 }
2532
2533 WebInspector.NetworkDataGridNode.prototype = {
2534     /**
2535      * @return {!WebInspector.NetworkRequest}
2536      */
2537     request: function()
2538     {
2539         return this._request;
2540     },
2541
2542     /**
2543      * @override
2544      * @return {number}
2545      */
2546     nodeSelfHeight: function() {
2547         return this._parentView.rowHeight();
2548     },
2549
2550     /** override */
2551     createCells: function()
2552     {
2553         this._nameCell = null;
2554         this._timelineCell = null;
2555
2556         this._element.classList.toggle("network-error-row", this._isFailed());
2557         WebInspector.SortableDataGridNode.prototype.createCells.call(this);
2558
2559         this._updateGraph();
2560     },
2561
2562     /**
2563      * @override
2564      * @param {string} columnIdentifier
2565      * @return {!Element}
2566      */
2567     createCell: function(columnIdentifier)
2568     {
2569         var cell = this.createTD(columnIdentifier);
2570         switch (columnIdentifier) {
2571         case "name": this._renderNameCell(cell); break;
2572         case "timeline": this._createTimelineBar(cell); break;
2573         case "method": cell.setTextAndTitle(this._request.requestMethod); break;
2574         case "status": this._renderStatusCell(cell); break;
2575         case "scheme": cell.setTextAndTitle(this._request.scheme); break;
2576         case "domain": cell.setTextAndTitle(this._request.domain); break;
2577         case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
2578         case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
2579         case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
2580         case "connectionId": cell.setTextAndTitle(this._request.connectionId); break;
2581         case "type": cell.setTextAndTitle(this._request.mimeType || this._request.requestContentType() || ""); break;
2582         case "initiator": this._renderInitiatorCell(cell); break;
2583         case "size": this._renderSizeCell(cell); break;
2584         case "time": this._renderTimeCell(cell); break;
2585         default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
2586         }
2587
2588         return cell;
2589     },
2590
2591     /**
2592      * @param {?Array} array
2593      * @return {string}
2594      */
2595     _arrayLength: function(array)
2596     {
2597         return array ? "" + array.length : "";
2598     },
2599
2600     /**
2601      * @override
2602      * @protected
2603      */
2604     willAttach: function()
2605     {
2606         if (this._staleGraph)
2607             this._updateGraph();
2608     },
2609
2610     wasDetached: function()
2611     {
2612         if (this._linkifiedInitiatorAnchor)
2613             this._linkifiedInitiatorAnchor.remove();
2614     },
2615
2616     dispose: function()
2617     {
2618         this._linkifier.reset();
2619     },
2620
2621     _onClick: function()
2622     {
2623         if (!this._parentView.allowRequestSelection())
2624             this.select();
2625     },
2626
2627     select: function()
2628     {
2629         this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
2630         WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
2631
2632         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
2633             action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
2634             url: this._request.url
2635         });
2636     },
2637
2638     /**
2639      * @param {!RegExp=} regexp
2640      * @return {!Array.<!Object>}
2641      */
2642     highlightMatchedSubstring: function(regexp)
2643     {
2644         // Ensure element is created.
2645         this.element();
2646         var domChanges = [];
2647         var matchInfo = this._nameCell.textContent.match(regexp);
2648         if (matchInfo)
2649             WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
2650         return domChanges;
2651     },
2652
2653     _openInNewTab: function()
2654     {
2655         InspectorFrontendHost.openInNewTab(this._request.url);
2656     },
2657
2658     get selectable()
2659     {
2660         return this._parentView.allowRequestSelection();
2661     },
2662
2663     /**
2664      * @param {!Element} cell
2665      */
2666     _createTimelineBar: function(cell)
2667     {
2668         cell = cell.createChild("div");
2669         this._timelineCell = cell;
2670
2671         cell.className = "network-graph-side";
2672
2673         this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
2674         this._barAreaElement.request = this._request;
2675
2676         var type = this._request.type.name();
2677         var cached = this._request.cached;
2678
2679         this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
2680         this._barLeftElement.classList.add(type, "waiting");
2681         this._barLeftElement.classList.toggle("cached", cached);
2682
2683         this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
2684         this._barRightElement.classList.add(type);
2685         this._barRightElement.classList.toggle("cached", cached);
2686
2687         this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
2688         this._labelLeftElement.classList.add("waiting");
2689
2690         this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
2691
2692         cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
2693     },
2694
2695     /**
2696      * @return {boolean}
2697      */
2698     _isFailed: function()
2699     {
2700         return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
2701     },
2702
2703     /**
2704      * @param {!Element} cell
2705      */
2706     _renderNameCell: function(cell)
2707     {
2708         this._nameCell = cell;
2709         cell.addEventListener("click", this._onClick.bind(this), false);
2710         cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
2711         var iconElement;
2712         if (this._request.type === WebInspector.resourceTypes.Image) {
2713             var previewImage = document.createElementWithClass("img", "image-network-icon-preview");
2714             this._request.populateImageSource(previewImage);
2715
2716             iconElement = document.createElementWithClass("div", "icon");
2717             iconElement.appendChild(previewImage);
2718         } else {
2719             iconElement = document.createElementWithClass("img", "icon");
2720         }
2721         iconElement.classList.add(this._request.type.name());
2722
2723         cell.appendChild(iconElement);
2724         cell.createTextChild(this._request.name());
2725         this._appendSubtitle(cell, this._request.path());
2726         cell.title = this._request.url;
2727     },
2728
2729     /**
2730      * @param {!Element} cell
2731      */
2732     _renderStatusCell: function(cell)
2733     {
2734         cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached || !this._request.statusCode));
2735
2736         if (this._request.failed && !this._request.canceled) {
2737             var failText = WebInspector.UIString("(failed)");
2738             if (this._request.localizedFailDescription) {
2739                 cell.createTextChild(failText);
2740                 this._appendSubtitle(cell, this._request.localizedFailDescription);
2741                 cell.title = failText + " " + this._request.localizedFailDescription;
2742             } else
2743                 cell.setTextAndTitle(failText);
2744         } else if (this._request.statusCode) {
2745             cell.createTextChild("" + this._request.statusCode);
2746             this._appendSubtitle(cell, this._request.statusText);
2747             cell.title = this._request.statusCode + " " + this._request.statusText;
2748         } else if (this._request.parsedURL.isDataURL()) {
2749             cell.setTextAndTitle(WebInspector.UIString("(data)"));
2750         } else if (this._request.canceled) {
2751             cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
2752         } else if (this._request.finished) {
2753             cell.setTextAndTitle(WebInspector.UIString("Finished"));
2754         } else {
2755             cell.setTextAndTitle(WebInspector.UIString("(pending)"));
2756         }
2757     },
2758
2759     /**
2760      * @param {!Element} cell
2761      */
2762     _renderInitiatorCell: function(cell)
2763     {
2764         var request = this._request;
2765         var initiator = request.initiatorInfo();
2766
2767         switch (initiator.type) {
2768         case WebInspector.NetworkRequest.InitiatorType.Parser:
2769             cell.title = initiator.url + ":" + initiator.lineNumber;
2770             cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
2771             this._appendSubtitle(cell, WebInspector.UIString("Parser"));
2772             break;
2773
2774         case WebInspector.NetworkRequest.InitiatorType.Redirect:
2775             cell.title = initiator.url;
2776             console.assert(request.redirectSource);
2777             var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
2778             cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
2779             this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
2780             break;
2781
2782         case WebInspector.NetworkRequest.InitiatorType.Script:
2783             if (!this._linkifiedInitiatorAnchor) {
2784                 this._linkifiedInitiatorAnchor = this._linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
2785                 this._linkifiedInitiatorAnchor.title = "";
2786             }
2787             cell.appendChild(this._linkifiedInitiatorAnchor);
2788             this._appendSubtitle(cell, WebInspector.UIString("Script"));
2789             cell.classList.add("network-script-initiated");
2790             cell.request = request;
2791             break;
2792
2793         default:
2794             cell.title = "";
2795             cell.classList.add("network-dim-cell");
2796             cell.setTextAndTitle(WebInspector.UIString("Other"));
2797         }
2798     },
2799
2800     /**
2801      * @param {!Element} cell
2802      */
2803     _renderSizeCell: function(cell)
2804     {
2805         if (this._request.fetchedViaServiceWorker) {
2806             cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)"));
2807             cell.classList.add("network-dim-cell");
2808         } else if (this._request.cached) {
2809             cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
2810             cell.classList.add("network-dim-cell");
2811         } else {
2812             var resourceSize = Number.bytesToString(this._request.resourceSize);
2813             var transferSize = Number.bytesToString(this._request.transferSize);
2814             cell.setTextAndTitle(transferSize);
2815             this._appendSubtitle(cell, resourceSize);
2816         }
2817     },
2818
2819     /**
2820      * @param {!Element} cell
2821      */
2822     _renderTimeCell: function(cell)
2823     {
2824         if (this._request.duration > 0) {
2825             cell.setTextAndTitle(Number.secondsToString(this._request.duration));
2826             this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
2827         } else {
2828             cell.classList.add("network-dim-cell");
2829             cell.setTextAndTitle(WebInspector.UIString("Pending"));
2830         }
2831     },
2832
2833     /**
2834      * @param {!Element} cellElement
2835      * @param {string} subtitleText
2836      */
2837     _appendSubtitle: function(cellElement, subtitleText)
2838     {
2839         var subtitleElement = document.createElement("div");
2840         subtitleElement.className = "network-cell-subtitle";
2841         subtitleElement.textContent = subtitleText;
2842         cellElement.appendChild(subtitleElement);
2843     },
2844
2845     refreshGraph: function()
2846     {
2847         if (!this._timelineCell)
2848             return;
2849         this._staleGraph = true;
2850         if (this.attached())
2851             this.dataGrid.scheduleUpdate();
2852     },
2853
2854     _updateGraph: function()
2855     {
2856         this._staleGraph = false;
2857         if (!this._timelineCell)
2858             return;
2859
2860         var calculator = this._parentView.calculator();
2861         var percentages = calculator.computeBarGraphPercentages(this._request);
2862         this._percentages = percentages;
2863
2864         this._barAreaElement.classList.remove("hidden");
2865
2866         this._barLeftElement.style.setProperty("left", percentages.start + "%");
2867         this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
2868
2869         this._barRightElement.style.setProperty("left", percentages.middle + "%");
2870         this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
2871
2872         var labels = calculator.computeBarGraphLabels(this._request);
2873         this._labelLeftElement.textContent = labels.left;
2874         this._labelRightElement.textContent = labels.right;
2875
2876         var tooltip = (labels.tooltip || "");
2877         this._barLeftElement.title = tooltip;
2878         this._labelLeftElement.title = tooltip;
2879         this._labelRightElement.title = tooltip;
2880         this._barRightElement.title = tooltip;
2881     },
2882
2883     _refreshLabelPositions: function()
2884     {
2885         if (!this._percentages)
2886             return;
2887         this._labelLeftElement.style.removeProperty("left");
2888         this._labelLeftElement.style.removeProperty("right");
2889         this._labelLeftElement.classList.remove("before");
2890         this._labelLeftElement.classList.remove("hidden");
2891
2892         this._labelRightElement.style.removeProperty("left");
2893         this._labelRightElement.style.removeProperty("right");
2894         this._labelRightElement.classList.remove("after");
2895         this._labelRightElement.classList.remove("hidden");
2896
2897         const labelPadding = 10;
2898         const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
2899         const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
2900
2901         if (this._barLeftElement) {
2902             var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
2903             var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
2904         } else {
2905             var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
2906             var rightBarWidth = barRightElementOffsetWidth - labelPadding;
2907         }
2908
2909         const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
2910         const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
2911
2912         const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
2913         const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
2914         const graphElementOffsetWidth = this._timelineCell.offsetWidth;
2915
2916         if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
2917             var leftHidden = true;
2918
2919         if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
2920             var rightHidden = true;
2921
2922         if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
2923             // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
2924             if (labelBefore && !labelAfter)
2925                 leftHidden = true;
2926             else if (labelAfter && !labelBefore)
2927                 rightHidden = true;
2928         }
2929
2930         if (labelBefore) {
2931             if (leftHidden)
2932                 this._labelLeftElement.classList.add("hidden");
2933             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
2934             this._labelLeftElement.classList.add("before");
2935         } else {
2936             this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
2937             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
2938         }
2939
2940         if (labelAfter) {
2941             if (rightHidden)
2942                 this._labelRightElement.classList.add("hidden");
2943             this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
2944             this._labelRightElement.classList.add("after");
2945         } else {
2946             this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
2947             this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
2948         }
2949     },
2950
2951     __proto__: WebInspector.SortableDataGridNode.prototype
2952 }
2953
2954 /**
2955  * @param {!WebInspector.NetworkDataGridNode} a
2956  * @param {!WebInspector.NetworkDataGridNode} b
2957  * @return {number}
2958  */
2959 WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
2960 {
2961     var aFileName = a._request.name();
2962     var bFileName = b._request.name();
2963     if (aFileName > bFileName)
2964         return 1;
2965     if (bFileName > aFileName)
2966         return -1;
2967     return a._request.indentityCompare(b._request);
2968 }
2969
2970 /**
2971  * @param {!WebInspector.NetworkDataGridNode} a
2972  * @param {!WebInspector.NetworkDataGridNode} b
2973  * @return {number}
2974  */
2975 WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
2976 {
2977     var aRemoteAddress = a._request.remoteAddress();
2978     var bRemoteAddress = b._request.remoteAddress();
2979     if (aRemoteAddress > bRemoteAddress)
2980         return 1;
2981     if (bRemoteAddress > aRemoteAddress)
2982         return -1;
2983     return a._request.indentityCompare(b._request);
2984 }
2985
2986 /**
2987  * @param {!WebInspector.NetworkDataGridNode} a
2988  * @param {!WebInspector.NetworkDataGridNode} b
2989  * @return {number}
2990  */
2991 WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
2992 {
2993     if (b._request.cached && !a._request.cached)
2994         return 1;
2995     if (a._request.cached && !b._request.cached)
2996         return -1;
2997     return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
2998 }
2999
3000 /**
3001  * @param {!WebInspector.NetworkDataGridNode} a
3002  * @param {!WebInspector.NetworkDataGridNode} b
3003  * @return {number}
3004  */
3005 WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
3006 {
3007     var aInitiator = a._request.initiatorInfo();
3008     var bInitiator = b._request.initiatorInfo();
3009
3010     if (aInitiator.type < bInitiator.type)
3011         return -1;
3012     if (aInitiator.type > bInitiator.type)
3013         return 1;
3014
3015     if (typeof aInitiator.__source === "undefined")
3016         aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
3017     if (typeof bInitiator.__source === "undefined")
3018         bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
3019
3020     if (aInitiator.__source < bInitiator.__source)
3021         return -1;
3022     if (aInitiator.__source > bInitiator.__source)
3023         return 1;
3024
3025     if (aInitiator.lineNumber < bInitiator.lineNumber)
3026         return -1;
3027     if (aInitiator.lineNumber > bInitiator.lineNumber)
3028         return 1;
3029
3030     if (aInitiator.columnNumber < bInitiator.columnNumber)
3031         return -1;
3032     if (aInitiator.columnNumber > bInitiator.columnNumber)
3033         return 1;
3034
3035     return a._request.indentityCompare(b._request);
3036 }
3037
3038 /**
3039  * @param {!WebInspector.NetworkDataGridNode} a
3040  * @param {!WebInspector.NetworkDataGridNode} b
3041  * @return {number}
3042  */
3043 WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
3044 {
3045     var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
3046     var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
3047     return (aScore - bScore) || a._request.indentityCompare(b._request);
3048 }
3049
3050 /**
3051  * @param {!WebInspector.NetworkDataGridNode} a
3052  * @param {!WebInspector.NetworkDataGridNode} b
3053  * @return {number}
3054  */
3055 WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
3056 {
3057     var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
3058     var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
3059     return (aScore - bScore) || a._request.indentityCompare(b._request);
3060 }
3061
3062 /**
3063  * @param {string} propertyName
3064  * @param {boolean} revert
3065  * @param {!WebInspector.NetworkDataGridNode} a
3066  * @param {!WebInspector.NetworkDataGridNode} b
3067  * @return {number}
3068  */
3069 WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
3070 {
3071     var aValue = a._request[propertyName];
3072     var bValue = b._request[propertyName];
3073     if (aValue > bValue)
3074         return revert ? -1 : 1;
3075     if (bValue > aValue)
3076         return revert ? 1 : -1;
3077     return a._request.indentityCompare(b._request);
3078 }