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