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