Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / NetworkRequest.js
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     __proto__: WebInspector.SDKObject.prototype
1007 }