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