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