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