1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.Object} 34 * @implements {WebInspector.ContentProvider} 35 * @param {NetworkAgent.RequestId} requestId 36 * @param {string} url 37 * @param {string} documentURL 38 * @param {NetworkAgent.FrameId} frameId 39 * @param {NetworkAgent.LoaderId} loaderId 40 */ 41 WebInspector.NetworkRequest = function(requestId, url, documentURL, frameId, loaderId) 42 { 43 this._requestId = requestId; 44 this.url = url; 45 this._documentURL = documentURL; 46 this._frameId = frameId; 47 this._loaderId = loaderId; 48 this._startTime = -1; 49 this._endTime = -1; 50 51 this.statusCode = 0; 52 this.statusText = ""; 53 this.requestMethod = ""; 54 this.requestTime = 0; 55 this.receiveHeadersEnd = 0; 56 57 this._type = WebInspector.resourceTypes.Other; 58 this._contentEncoded = false; 59 this._pendingContentCallbacks = []; 60 this._frames = []; 61 62 this._responseHeaderValues = {}; 63 } 64 65 WebInspector.NetworkRequest.Events = { 66 FinishedLoading: "FinishedLoading", 67 TimingChanged: "TimingChanged", 68 RequestHeadersChanged: "RequestHeadersChanged", 69 ResponseHeadersChanged: "ResponseHeadersChanged", 70 } 71 72 /** @enum {string} */ 73 WebInspector.NetworkRequest.InitiatorType = { 74 Other: "other", 75 Parser: "parser", 76 Redirect: "redirect", 77 Script: "script" 78 } 79 80 /** @typedef {{name: string, value: string}} */ 81 WebInspector.NetworkRequest.NameValue; 82 83 WebInspector.NetworkRequest.prototype = { 84 /** 85 * @return {NetworkAgent.RequestId} 86 */ 87 get requestId() 88 { 89 return this._requestId; 90 }, 91 92 set requestId(requestId) 93 { 94 this._requestId = requestId; 95 }, 96 97 /** 98 * @return {string} 99 */ 100 get url() 101 { 102 return this._url; 103 }, 104 105 set url(x) 106 { 107 if (this._url === x) 108 return; 109 110 this._url = x; 111 this._parsedURL = new WebInspector.ParsedURL(x); 112 delete this._parsedQueryParameters; 113 delete this._name; 114 delete this._path; 115 }, 116 117 /** 118 * @return {string} 119 */ 120 get documentURL() 121 { 122 return this._documentURL; 123 }, 124 125 get parsedURL() 126 { 127 return this._parsedURL; 128 }, 129 130 /** 131 * @return {NetworkAgent.FrameId} 132 */ 133 get frameId() 134 { 135 return this._frameId; 136 }, 137 138 /** 139 * @return {NetworkAgent.LoaderId} 140 */ 141 get loaderId() 142 { 143 return this._loaderId; 144 }, 145 146 /** 147 * @return {number} 148 */ 149 get startTime() 150 { 151 return this._startTime || -1; 152 }, 153 154 set startTime(x) 155 { 156 this._startTime = x; 157 }, 158 159 /** 160 * @return {number} 161 */ 162 get responseReceivedTime() 163 { 164 return this._responseReceivedTime || -1; 165 }, 166 167 set responseReceivedTime(x) 168 { 169 this._responseReceivedTime = x; 170 }, 171 172 /** 173 * @return {number} 174 */ 175 get endTime() 176 { 177 return this._endTime || -1; 178 }, 179 180 set endTime(x) 181 { 182 if (this.timing && this.timing.requestTime) { 183 // Check against accurate responseReceivedTime. 184 this._endTime = Math.max(x, this.responseReceivedTime); 185 } else { 186 // Prefer endTime since it might be from the network stack. 187 this._endTime = x; 188 if (this._responseReceivedTime > x) 189 this._responseReceivedTime = x; 190 } 191 }, 192 193 /** 194 * @return {number} 195 */ 196 get duration() 197 { 198 if (this._endTime === -1 || this._startTime === -1) 199 return -1; 200 return this._endTime - this._startTime; 201 }, 202 203 /** 204 * @return {number} 205 */ 206 get latency() 207 { 208 if (this._responseReceivedTime === -1 || this._startTime === -1) 209 return -1; 210 return this._responseReceivedTime - this._startTime; 211 }, 212 213 /** 214 * @return {number} 215 */ 216 get receiveDuration() 217 { 218 if (this._endTime === -1 || this._responseReceivedTime === -1) 219 return -1; 220 return this._endTime - this._responseReceivedTime; 221 }, 222 223 /** 224 * @return {number} 225 */ 226 get resourceSize() 227 { 228 return this._resourceSize || 0; 229 }, 230 231 set resourceSize(x) 232 { 233 this._resourceSize = x; 234 }, 235 236 /** 237 * @return {number} 238 */ 239 get transferSize() 240 { 241 if (typeof this._transferSize === "number") 242 return this._transferSize; 243 if (this.statusCode === 304) // Not modified 244 return this.responseHeadersSize; 245 if (this._cached) 246 return 0; 247 // If we did not receive actual transfer size from network 248 // stack, we prefer using Content-Length over resourceSize as 249 // resourceSize may differ from actual transfer size if platform's 250 // network stack performed decoding (e.g. gzip decompression). 251 // The Content-Length, though, is expected to come from raw 252 // response headers and will reflect actual transfer length. 253 // This won't work for chunked content encoding, so fall back to 254 // resourceSize when we don't have Content-Length. This still won't 255 // work for chunks with non-trivial encodings. We need a way to 256 // get actual transfer size from the network stack. 257 var bodySize = Number(this.responseHeaderValue("Content-Length") || this.resourceSize); 258 return this.responseHeadersSize + bodySize; 259 }, 260 261 /** 262 * @param {number} x 263 */ 264 increaseTransferSize: function(x) 265 { 266 this._transferSize = (this._transferSize || 0) + x; 267 }, 268 269 /** 270 * @return {boolean} 271 */ 272 get finished() 273 { 274 return this._finished; 275 }, 276 277 set finished(x) 278 { 279 if (this._finished === x) 280 return; 281 282 this._finished = x; 283 284 if (x) { 285 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.FinishedLoading, this); 286 if (this._pendingContentCallbacks.length) 287 this._innerRequestContent(); 288 } 289 }, 290 291 /** 292 * @return {boolean} 293 */ 294 get failed() 295 { 296 return this._failed; 297 }, 298 299 set failed(x) 300 { 301 this._failed = x; 302 }, 303 304 /** 305 * @return {boolean} 306 */ 307 get canceled() 308 { 309 return this._canceled; 310 }, 311 312 set canceled(x) 313 { 314 this._canceled = x; 315 }, 316 317 /** 318 * @return {boolean} 319 */ 320 get cached() 321 { 322 return this._cached && !this._transferSize; 323 }, 324 325 set cached(x) 326 { 327 this._cached = x; 328 if (x) 329 delete this._timing; 330 }, 331 332 /** 333 * @return {NetworkAgent.ResourceTiming|undefined} 334 */ 335 get timing() 336 { 337 return this._timing; 338 }, 339 340 set timing(x) 341 { 342 if (x && !this._cached) { 343 // Take startTime and responseReceivedTime from timing data for better accuracy. 344 // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis. 345 this._startTime = x.requestTime; 346 this._responseReceivedTime = x.requestTime + x.receiveHeadersEnd / 1000.0; 347 348 this._timing = x; 349 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.TimingChanged, this); 350 } 351 }, 352 353 /** 354 * @return {string} 355 */ 356 get mimeType() 357 { 358 return this._mimeType; 359 }, 360 361 set mimeType(x) 362 { 363 this._mimeType = x; 364 }, 365 366 /** 367 * @return {string} 368 */ 369 get displayName() 370 { 371 return this._parsedURL.displayName; 372 }, 373 374 name: function() 375 { 376 if (this._name) 377 return this._name; 378 this._parseNameAndPathFromURL(); 379 return this._name; 380 }, 381 382 path: function() 383 { 384 if (this._path) 385 return this._path; 386 this._parseNameAndPathFromURL(); 387 return this._path; 388 }, 389 390 _parseNameAndPathFromURL: function() 391 { 392 if (this._parsedURL.isDataURL()) { 393 this._name = this._parsedURL.dataURLDisplayName(); 394 this._path = ""; 395 } else if (this._parsedURL.isAboutBlank()) { 396 this._name = this._parsedURL.url; 397 this._path = ""; 398 } else { 399 this._path = this._parsedURL.host + this._parsedURL.folderPathComponents; 400 this._path = this._path.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : ""); 401 if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams) 402 this._name = this._parsedURL.lastPathComponent + (this._parsedURL.queryParams ? "?" + this._parsedURL.queryParams : ""); 403 else if (this._parsedURL.folderPathComponents) { 404 this._name = this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf("/") + 1) + "/"; 405 this._path = this._path.substring(0, this._path.lastIndexOf("/")); 406 } else { 407 this._name = this._parsedURL.host; 408 this._path = ""; 409 } 410 } 411 }, 412 413 /** 414 * @return {string} 415 */ 416 get folder() 417 { 418 var path = this._parsedURL.path; 419 var indexOfQuery = path.indexOf("?"); 420 if (indexOfQuery !== -1) 421 path = path.substring(0, indexOfQuery); 422 var lastSlashIndex = path.lastIndexOf("/"); 423 return lastSlashIndex !== -1 ? path.substring(0, lastSlashIndex) : ""; 424 }, 425 426 /** 427 * @return {WebInspector.ResourceType} 428 */ 429 get type() 430 { 431 return this._type; 432 }, 433 434 set type(x) 435 { 436 this._type = x; 437 }, 438 439 /** 440 * @return {string} 441 */ 442 get domain() 443 { 444 return this._parsedURL.host; 445 }, 446 447 /** 448 * @return {?WebInspector.NetworkRequest} 449 */ 450 get redirectSource() 451 { 452 if (this.redirects && this.redirects.length > 0) 453 return this.redirects[this.redirects.length - 1]; 454 return this._redirectSource; 455 }, 456 457 set redirectSource(x) 458 { 459 this._redirectSource = x; 460 delete this._initiatorInfo; 461 }, 462 463 /** 464 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 465 */ 466 get requestHeaders() 467 { 468 return this._requestHeaders || []; 469 }, 470 471 set requestHeaders(x) 472 { 473 this._requestHeaders = x; 474 delete this._sortedRequestHeaders; 475 delete this._requestCookies; 476 477 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged); 478 }, 479 480 /** 481 * @return {string} 482 */ 483 get requestHeadersText() 484 { 485 if (typeof this._requestHeadersText === "undefined") { 486 this._requestHeadersText = this.requestMethod + " " + this.url + " HTTP/1.1\r\n"; 487 for (var i = 0; i < this.requestHeaders.length; ++i) 488 this._requestHeadersText += this.requestHeaders[i].name + ": " + this.requestHeaders[i].value + "\r\n"; 489 } 490 return this._requestHeadersText; 491 }, 492 493 set requestHeadersText(x) 494 { 495 this._requestHeadersText = x; 496 497 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged); 498 }, 499 500 /** 501 * @return {number} 502 */ 503 get requestHeadersSize() 504 { 505 return this.requestHeadersText.length; 506 }, 507 508 /** 509 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 510 */ 511 get sortedRequestHeaders() 512 { 513 if (this._sortedRequestHeaders !== undefined) 514 return this._sortedRequestHeaders; 515 516 this._sortedRequestHeaders = []; 517 this._sortedRequestHeaders = this.requestHeaders.slice(); 518 this._sortedRequestHeaders.sort(function(a,b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()) }); 519 return this._sortedRequestHeaders; 520 }, 521 522 /** 523 * @param {string} headerName 524 * @return {string|undefined} 525 */ 526 requestHeaderValue: function(headerName) 527 { 528 return this._headerValue(this.requestHeaders, headerName); 529 }, 530 531 /** 532 * @return {Array.<WebInspector.Cookie>} 533 */ 534 get requestCookies() 535 { 536 if (!this._requestCookies) 537 this._requestCookies = WebInspector.CookieParser.parseCookie(this.requestHeaderValue("Cookie")); 538 return this._requestCookies; 539 }, 540 541 /** 542 * @return {string|undefined} 543 */ 544 get requestFormData() 545 { 546 return this._requestFormData; 547 }, 548 549 set requestFormData(x) 550 { 551 this._requestFormData = x; 552 delete this._parsedFormParameters; 553 }, 554 555 /** 556 * @return {string|undefined} 557 */ 558 get requestHttpVersion() 559 { 560 var firstLine = this.requestHeadersText.split(/\r\n/)[0]; 561 var match = firstLine.match(/(HTTP\/\d+\.\d+)$/); 562 return match ? match[1] : undefined; 563 }, 564 565 /** 566 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 567 */ 568 get responseHeaders() 569 { 570 return this._responseHeaders || []; 571 }, 572 573 set responseHeaders(x) 574 { 575 this._responseHeaders = x; 576 delete this._sortedResponseHeaders; 577 delete this._responseCookies; 578 this._responseHeaderValues = {}; 579 580 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged); 581 }, 582 583 /** 584 * @return {string} 585 */ 586 get responseHeadersText() 587 { 588 if (typeof this._responseHeadersText === "undefined") { 589 this._responseHeadersText = "HTTP/1.1 " + this.statusCode + " " + this.statusText + "\r\n"; 590 for (var i = 0; i < this.responseHeaders.length; ++i) 591 this._responseHeadersText += this.responseHeaders[i].name + ": " + this.responseHeaders[i].value + "\r\n"; 592 } 593 return this._responseHeadersText; 594 }, 595 596 set responseHeadersText(x) 597 { 598 this._responseHeadersText = x; 599 600 this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged); 601 }, 602 603 /** 604 * @return {number} 605 */ 606 get responseHeadersSize() 607 { 608 return this.responseHeadersText.length; 609 }, 610 611 /** 612 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 613 */ 614 get sortedResponseHeaders() 615 { 616 if (this._sortedResponseHeaders !== undefined) 617 return this._sortedResponseHeaders; 618 619 this._sortedResponseHeaders = []; 620 this._sortedResponseHeaders = this.responseHeaders.slice(); 621 this._sortedResponseHeaders.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); }); 622 return this._sortedResponseHeaders; 623 }, 624 625 /** 626 * @param {string} headerName 627 * @return {string|undefined} 628 */ 629 responseHeaderValue: function(headerName) 630 { 631 var value = this._responseHeaderValues[headerName]; 632 if (value === undefined) { 633 value = this._headerValue(this.responseHeaders, headerName); 634 this._responseHeaderValues[headerName] = (value !== undefined) ? value : null; 635 } 636 return (value !== null) ? value : undefined; 637 }, 638 639 /** 640 * @return {Array.<WebInspector.Cookie>} 641 */ 642 get responseCookies() 643 { 644 if (!this._responseCookies) 645 this._responseCookies = WebInspector.CookieParser.parseSetCookie(this.responseHeaderValue("Set-Cookie")); 646 return this._responseCookies; 647 }, 648 649 /** 650 * @return {?string} 651 */ 652 queryString: function() 653 { 654 if (this._queryString) 655 return this._queryString; 656 var queryString = this.url.split("?", 2)[1]; 657 if (!queryString) 658 return null; 659 this._queryString = queryString.split("#", 2)[0]; 660 return this._queryString; 661 }, 662 663 /** 664 * @return {?Array.<!WebInspector.NetworkRequest.NameValue>} 665 */ 666 get queryParameters() 667 { 668 if (this._parsedQueryParameters) 669 return this._parsedQueryParameters; 670 var queryString = this.queryString(); 671 if (!queryString) 672 return null; 673 this._parsedQueryParameters = this._parseParameters(queryString); 674 return this._parsedQueryParameters; 675 }, 676 677 /** 678 * @return {?Array.<!WebInspector.NetworkRequest.NameValue>} 679 */ 680 get formParameters() 681 { 682 if (this._parsedFormParameters) 683 return this._parsedFormParameters; 684 if (!this.requestFormData) 685 return null; 686 var requestContentType = this.requestContentType(); 687 if (!requestContentType || !requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) 688 return null; 689 this._parsedFormParameters = this._parseParameters(this.requestFormData); 690 return this._parsedFormParameters; 691 }, 692 693 /** 694 * @return {string|undefined} 695 */ 696 get responseHttpVersion() 697 { 698 var match = this.responseHeadersText.match(/^(HTTP\/\d+\.\d+)/); 699 return match ? match[1] : undefined; 700 }, 701 702 /** 703 * @param {string} queryString 704 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 705 */ 706 _parseParameters: function(queryString) 707 { 708 function parseNameValue(pair) 709 { 710 var splitPair = pair.split("=", 2); 711 return {name: splitPair[0], value: splitPair[1] || ""}; 712 } 713 return queryString.split("&").map(parseNameValue); 714 }, 715 716 /** 717 * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers 718 * @param {string} headerName 719 * @return {string|undefined} 720 */ 721 _headerValue: function(headers, headerName) 722 { 723 headerName = headerName.toLowerCase(); 724 725 var values = []; 726 for (var i = 0; i < headers.length; ++i) { 727 if (headers[i].name.toLowerCase() === headerName) 728 values.push(headers[i].value); 729 } 730 if (!values.length) 731 return undefined; 732 // Set-Cookie values should be separated by '\n', not comma, otherwise cookies could not be parsed. 733 if (headerName === "set-cookie") 734 return values.join("\n"); 735 return values.join(", "); 736 }, 737 738 /** 739 * @return {?string|undefined} 740 */ 741 get content() 742 { 743 return this._content; 744 }, 745 746 /** 747 * @return {boolean} 748 */ 749 get contentEncoded() 750 { 751 return this._contentEncoded; 752 }, 753 754 /** 755 * @return {string} 756 */ 757 contentURL: function() 758 { 759 return this._url; 760 }, 761 762 /** 763 * @return {WebInspector.ResourceType} 764 */ 765 contentType: function() 766 { 767 return this._type; 768 }, 769 770 /** 771 * @param {function(?string, boolean, string)} callback 772 */ 773 requestContent: function(callback) 774 { 775 // We do not support content retrieval for WebSockets at the moment. 776 // Since WebSockets are potentially long-living, fail requests immediately 777 // to prevent caller blocking until resource is marked as finished. 778 if (this.type === WebInspector.resourceTypes.WebSocket) { 779 callback(null, false, this._mimeType); 780 return; 781 } 782 if (typeof this._content !== "undefined") { 783 callback(this.content || null, this._contentEncoded, this.type.canonicalMimeType() || this._mimeType); 784 return; 785 } 786 this._pendingContentCallbacks.push(callback); 787 if (this.finished) 788 this._innerRequestContent(); 789 }, 790 791 /** 792 * @param {string} query 793 * @param {boolean} caseSensitive 794 * @param {boolean} isRegex 795 * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback 796 */ 797 searchInContent: function(query, caseSensitive, isRegex, callback) 798 { 799 callback([]); 800 }, 801 802 /** 803 * @return {boolean} 804 */ 805 isHttpFamily: function() 806 { 807 return !!this.url.match(/^https?:/i); 808 }, 809 810 /** 811 * @return {string|undefined} 812 */ 813 requestContentType: function() 814 { 815 return this.requestHeaderValue("Content-Type"); 816 }, 817 818 /** 819 * @return {boolean} 820 */ 821 isPingRequest: function() 822 { 823 return "text/ping" === this.requestContentType(); 824 }, 825 826 /** 827 * @return {boolean} 828 */ 829 hasErrorStatusCode: function() 830 { 831 return this.statusCode >= 400; 832 }, 833 834 /** 835 * @param {Element} image 836 */ 837 populateImageSource: function(image) 838 { 839 /** 840 * @this {WebInspector.NetworkRequest} 841 * @param {?string} content 842 * @param {boolean} contentEncoded 843 * @param {string} mimeType 844 */ 845 function onResourceContent(content, contentEncoded, mimeType) 846 { 847 var imageSrc = this.asDataURL(); 848 if (imageSrc === null) 849 imageSrc = this.url; 850 image.src = imageSrc; 851 } 852 853 this.requestContent(onResourceContent.bind(this)); 854 }, 855 856 /** 857 * @return {?string} 858 */ 859 asDataURL: function() 860 { 861 return WebInspector.contentAsDataURL(this._content, this.mimeType, this._contentEncoded); 862 }, 863 864 _innerRequestContent: function() 865 { 866 if (this._contentRequested) 867 return; 868 this._contentRequested = true; 869 870 /** 871 * @param {?Protocol.Error} error 872 * @param {string} content 873 * @param {boolean} contentEncoded 874 */ 875 function onResourceContent(error, content, contentEncoded) 876 { 877 this._content = error ? null : content; 878 this._contentEncoded = contentEncoded; 879 var callbacks = this._pendingContentCallbacks.slice(); 880 for (var i = 0; i < callbacks.length; ++i) 881 callbacks[i](this._content, this._contentEncoded, this._mimeType); 882 this._pendingContentCallbacks.length = 0; 883 delete this._contentRequested; 884 } 885 NetworkAgent.getResponseBody(this._requestId, onResourceContent.bind(this)); 886 }, 887 888 /** 889 * @return {{type: WebInspector.NetworkRequest.InitiatorType, url: string, source: string, lineNumber: number, columnNumber: number}} 890 */ 891 initiatorInfo: function() 892 { 893 if (this._initiatorInfo) 894 return this._initiatorInfo; 895 896 var type = WebInspector.NetworkRequest.InitiatorType.Other; 897 var url = ""; 898 var lineNumber = -Infinity; 899 var columnNumber = -Infinity; 900 901 if (this.redirectSource) { 902 type = WebInspector.NetworkRequest.InitiatorType.Redirect; 903 url = this.redirectSource.url; 904 } else if (this.initiator) { 905 if (this.initiator.type === NetworkAgent.InitiatorType.Parser) { 906 type = WebInspector.NetworkRequest.InitiatorType.Parser; 907 url = this.initiator.url; 908 lineNumber = this.initiator.lineNumber; 909 } else if (this.initiator.type === NetworkAgent.InitiatorType.Script) { 910 var topFrame = this.initiator.stackTrace[0]; 911 if (topFrame.url) { 912 type = WebInspector.NetworkRequest.InitiatorType.Script; 913 url = topFrame.url; 914 lineNumber = topFrame.lineNumber; 915 columnNumber = topFrame.columnNumber; 916 } 917 } 918 } 919 920 this._initiatorInfo = {type: type, url: url, source: WebInspector.displayNameForURL(url), lineNumber: lineNumber, columnNumber: columnNumber}; 921 return this._initiatorInfo; 922 }, 923 924 /** 925 * @return {!Array.<!Object>} 926 */ 927 frames: function() 928 { 929 return this._frames; 930 }, 931 932 /** 933 * @param {number} position 934 * @return {Object|undefined} 935 */ 936 frame: function(position) 937 { 938 return this._frames[position]; 939 }, 940 941 /** 942 * @param {string} errorMessage 943 * @param {number} time 944 */ 945 addFrameError: function(errorMessage, time) 946 { 947 this._pushFrame({errorMessage: errorMessage, time: time}); 948 }, 949 950 /** 951 * @param {!NetworkAgent.WebSocketFrame} response 952 * @param {number} time 953 * @param {boolean} sent 954 */ 955 addFrame: function(response, time, sent) 956 { 957 response.time = time; 958 if (sent) 959 response.sent = sent; 960 this._pushFrame(response); 961 }, 962 963 /** 964 * @param {!Object} frameOrError 965 */ 966 _pushFrame: function(frameOrError) 967 { 968 if (this._frames.length >= 100) 969 this._frames.splice(0, 10); 970 this._frames.push(frameOrError); 971 }, 972 973 __proto__: WebInspector.Object.prototype 974 } 975