Home | History | Annotate | Download | only in front_end
      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