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