Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / 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.Object}
34  * @implements {WebInspector.ContentProvider}
35  * @param {!NetworkAgent.RequestId} requestId
36  * @param {string} url
37  * @param {string} documentURL
38  * @param {!PageAgent.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
56     this._type = WebInspector.resourceTypes.Other;
57     this._contentEncoded = false;
58     this._pendingContentCallbacks = [];
59     this._frames = [];
60
61     this._responseHeaderValues = {};
62 }
63
64 WebInspector.NetworkRequest.Events = {
65     FinishedLoading: "FinishedLoading",
66     TimingChanged: "TimingChanged",
67     RequestHeadersChanged: "RequestHeadersChanged",
68     ResponseHeadersChanged: "ResponseHeadersChanged",
69 }
70
71 /** @enum {string} */
72 WebInspector.NetworkRequest.InitiatorType = {
73     Other: "other",
74     Parser: "parser",
75     Redirect: "redirect",
76     Script: "script"
77 }
78
79 /** @typedef {!{name: string, value: string}} */
80 WebInspector.NetworkRequest.NameValue;
81
82 WebInspector.NetworkRequest.prototype = {
83     /**
84      * @return {!NetworkAgent.RequestId}
85      */
86     get requestId()
87     {
88         return this._requestId;
89     },
90
91     set requestId(requestId)
92     {
93         this._requestId = requestId;
94     },
95
96     /**
97      * @return {string}
98      */
99     get url()
100     {
101         return this._url;
102     },
103
104     set url(x)
105     {
106         if (this._url === x)
107             return;
108
109         this._url = x;
110         this._parsedURL = new WebInspector.ParsedURL(x);
111         delete this._queryString;
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 {!PageAgent.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 resourceSize()
217     {
218         return this._resourceSize || 0;
219     },
220
221     set resourceSize(x)
222     {
223         this._resourceSize = x;
224     },
225
226     /**
227      * @return {number}
228      */
229     get transferSize()
230     {
231         if (typeof this._transferSize === "number")
232             return this._transferSize;
233         if (this.statusCode === 304) // Not modified
234             return this.responseHeadersSize;
235         if (this._cached)
236             return 0;
237         // If we did not receive actual transfer size from network
238         // stack, we prefer using Content-Length over resourceSize as
239         // resourceSize may differ from actual transfer size if platform's
240         // network stack performed decoding (e.g. gzip decompression).
241         // The Content-Length, though, is expected to come from raw
242         // response headers and will reflect actual transfer length.
243         // This won't work for chunked content encoding, so fall back to
244         // resourceSize when we don't have Content-Length. This still won't
245         // work for chunks with non-trivial encodings. We need a way to
246         // get actual transfer size from the network stack.
247         var bodySize = Number(this.responseHeaderValue("Content-Length") || this.resourceSize);
248         return this.responseHeadersSize + bodySize;
249     },
250
251     /**
252      * @param {number} x
253      */
254     increaseTransferSize: function(x)
255     {
256         this._transferSize = (this._transferSize || 0) + x;
257     },
258
259     /**
260      * @return {boolean}
261      */
262     get finished()
263     {
264         return this._finished;
265     },
266
267     set finished(x)
268     {
269         if (this._finished === x)
270             return;
271
272         this._finished = x;
273
274         if (x) {
275             this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.FinishedLoading, this);
276             if (this._pendingContentCallbacks.length)
277                 this._innerRequestContent();
278         }
279     },
280
281     /**
282      * @return {boolean}
283      */
284     get failed()
285     {
286         return this._failed;
287     },
288
289     set failed(x)
290     {
291         this._failed = x;
292     },
293
294     /**
295      * @return {boolean}
296      */
297     get canceled()
298     {
299         return this._canceled;
300     },
301
302     set canceled(x)
303     {
304         this._canceled = x;
305     },
306
307     /**
308      * @return {boolean}
309      */
310     get cached()
311     {
312         return !!this._cached && !this._transferSize;
313     },
314
315     set cached(x)
316     {
317         this._cached = x;
318         if (x)
319             delete this._timing;
320     },
321
322     /**
323      * @return {!NetworkAgent.ResourceTiming|undefined}
324      */
325     get timing()
326     {
327         return this._timing;
328     },
329
330     set timing(x)
331     {
332         if (x && !this._cached) {
333             // Take startTime and responseReceivedTime from timing data for better accuracy.
334             // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
335             this._startTime = x.requestTime;
336             this._responseReceivedTime = x.requestTime + x.receiveHeadersEnd / 1000.0;
337
338             this._timing = x;
339             this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.TimingChanged, this);
340         }
341     },
342
343     /**
344      * @return {string}
345      */
346     get mimeType()
347     {
348         return this._mimeType;
349     },
350
351     set mimeType(x)
352     {
353         this._mimeType = x;
354     },
355
356     /**
357      * @return {string}
358      */
359     get displayName()
360     {
361         return this._parsedURL.displayName;
362     },
363
364     /**
365      * @return {string}
366      */
367     name: function()
368     {
369         if (this._name)
370             return this._name;
371         this._parseNameAndPathFromURL();
372         return this._name;
373     },
374
375     /**
376      * @return {string}
377      */
378     path: function()
379     {
380         if (this._path)
381             return this._path;
382         this._parseNameAndPathFromURL();
383         return this._path;
384     },
385
386     _parseNameAndPathFromURL: function()
387     {
388         if (this._parsedURL.isDataURL()) {
389             this._name = this._parsedURL.dataURLDisplayName();
390             this._path = "";
391         } else if (this._parsedURL.isAboutBlank()) {
392             this._name = this._parsedURL.url;
393             this._path = "";
394         } else {
395             this._path = this._parsedURL.host + this._parsedURL.folderPathComponents;
396             this._path = this._path.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : "");
397             if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams)
398                 this._name = this._parsedURL.lastPathComponent + (this._parsedURL.queryParams ? "?" + this._parsedURL.queryParams : "");
399             else if (this._parsedURL.folderPathComponents) {
400                 this._name = this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf("/") + 1) + "/";
401                 this._path = this._path.substring(0, this._path.lastIndexOf("/"));
402             } else {
403                 this._name = this._parsedURL.host;
404                 this._path = "";
405             }
406         }
407     },
408
409     /**
410      * @return {string}
411      */
412     get folder()
413     {
414         var path = this._parsedURL.path;
415         var indexOfQuery = path.indexOf("?");
416         if (indexOfQuery !== -1)
417             path = path.substring(0, indexOfQuery);
418         var lastSlashIndex = path.lastIndexOf("/");
419         return lastSlashIndex !== -1 ? path.substring(0, lastSlashIndex) : "";
420     },
421
422     /**
423      * @return {!WebInspector.ResourceType}
424      */
425     get type()
426     {
427         return this._type;
428     },
429
430     set type(x)
431     {
432         this._type = x;
433     },
434
435     /**
436      * @return {string}
437      */
438     get domain()
439     {
440         return this._parsedURL.host;
441     },
442
443     /**
444      * @return {string}
445      */
446     get scheme()
447     {
448         return this._parsedURL.scheme;
449     },
450
451     /**
452      * @return {?WebInspector.NetworkRequest}
453      */
454     get redirectSource()
455     {
456         if (this.redirects && this.redirects.length > 0)
457             return this.redirects[this.redirects.length - 1];
458         return this._redirectSource;
459     },
460
461     set redirectSource(x)
462     {
463         this._redirectSource = x;
464         delete this._initiatorInfo;
465     },
466
467     /**
468      * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
469      */
470     requestHeaders: function()
471     {
472         return this._requestHeaders || [];
473     },
474
475     /**
476      * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
477      */
478     setRequestHeaders: function(headers)
479     {
480         this._requestHeaders = headers;
481         delete this._requestCookies;
482
483         this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged);
484     },
485
486     /**
487      * @return {string|undefined}
488      */
489     requestHeadersText: function()
490     {
491         return this._requestHeadersText;
492     },
493
494     /**
495      * @param {string} text
496      */
497     setRequestHeadersText: function(text)
498     {
499         this._requestHeadersText = text;
500
501         this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged);
502     },
503
504     /**
505      * @param {string} headerName
506      * @return {string|undefined}
507      */
508     requestHeaderValue: function(headerName)
509     {
510         return this._headerValue(this.requestHeaders(), headerName);
511     },
512
513     /**
514      * @return {!Array.<!WebInspector.Cookie>}
515      */
516     get requestCookies()
517     {
518         if (!this._requestCookies)
519             this._requestCookies = WebInspector.CookieParser.parseCookie(this.requestHeaderValue("Cookie"));
520         return this._requestCookies;
521     },
522
523     /**
524      * @return {string|undefined}
525      */
526     get requestFormData()
527     {
528         return this._requestFormData;
529     },
530
531     set requestFormData(x)
532     {
533         this._requestFormData = x;
534         delete this._parsedFormParameters;
535     },
536
537     /**
538      * @return {string|undefined}
539      */
540     requestHttpVersion: function()
541     {
542         var headersText = this.requestHeadersText();
543         if (!headersText)
544             return undefined;
545         var firstLine = headersText.split(/\r\n/)[0];
546         var match = firstLine.match(/(HTTP\/\d+\.\d+)$/);
547         return match ? match[1] : undefined;
548     },
549
550     /**
551      * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
552      */
553     get responseHeaders()
554     {
555         return this._responseHeaders || [];
556     },
557
558     set responseHeaders(x)
559     {
560         this._responseHeaders = x;
561         delete this._sortedResponseHeaders;
562         delete this._responseCookies;
563         this._responseHeaderValues = {};
564
565         this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged);
566     },
567
568     /**
569      * @return {string}
570      */
571     get responseHeadersText()
572     {
573         if (typeof this._responseHeadersText === "undefined") {
574             this._responseHeadersText = "HTTP/1.1 " + this.statusCode + " " + this.statusText + "\r\n";
575             for (var i = 0; i < this.responseHeaders.length; ++i)
576                 this._responseHeadersText += this.responseHeaders[i].name + ": " + this.responseHeaders[i].value + "\r\n";
577         }
578         return this._responseHeadersText;
579     },
580
581     set responseHeadersText(x)
582     {
583         this._responseHeadersText = x;
584
585         this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged);
586     },
587
588     /**
589      * @return {number}
590      */
591     get responseHeadersSize()
592     {
593         return this.responseHeadersText.length;
594     },
595
596     /**
597      * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
598      */
599     get sortedResponseHeaders()
600     {
601         if (this._sortedResponseHeaders !== undefined)
602             return this._sortedResponseHeaders;
603
604         this._sortedResponseHeaders = this.responseHeaders.slice();
605         this._sortedResponseHeaders.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); });
606         return this._sortedResponseHeaders;
607     },
608
609     /**
610      * @param {string} headerName
611      * @return {string|undefined}
612      */
613     responseHeaderValue: function(headerName)
614     {
615         var value = this._responseHeaderValues[headerName];
616         if (value === undefined) {
617             value = this._headerValue(this.responseHeaders, headerName);
618             this._responseHeaderValues[headerName] = (value !== undefined) ? value : null;
619         }
620         return (value !== null) ? value : undefined;
621     },
622
623     /**
624      * @return {!Array.<!WebInspector.Cookie>}
625      */
626     get responseCookies()
627     {
628         if (!this._responseCookies)
629             this._responseCookies = WebInspector.CookieParser.parseSetCookie(this.responseHeaderValue("Set-Cookie"));
630         return this._responseCookies;
631     },
632
633     /**
634      * @return {?string}
635      */
636     queryString: function()
637     {
638         if (this._queryString !== undefined)
639             return this._queryString;
640
641         var queryString = null;
642         var url = this.url;
643         var questionMarkPosition = url.indexOf("?");
644         if (questionMarkPosition !== -1) {
645             queryString = url.substring(questionMarkPosition + 1);
646             var hashSignPosition = queryString.indexOf("#");
647             if (hashSignPosition !== -1)
648                 queryString = queryString.substring(0, hashSignPosition);
649         }
650         this._queryString = queryString;
651         return this._queryString;
652     },
653
654     /**
655      * @return {?Array.<!WebInspector.NetworkRequest.NameValue>}
656      */
657     get queryParameters()
658     {
659         if (this._parsedQueryParameters)
660             return this._parsedQueryParameters;
661         var queryString = this.queryString();
662         if (!queryString)
663             return null;
664         this._parsedQueryParameters = this._parseParameters(queryString);
665         return this._parsedQueryParameters;
666     },
667
668     /**
669      * @return {?Array.<!WebInspector.NetworkRequest.NameValue>}
670      */
671     get formParameters()
672     {
673         if (this._parsedFormParameters)
674             return this._parsedFormParameters;
675         if (!this.requestFormData)
676             return null;
677         var requestContentType = this.requestContentType();
678         if (!requestContentType || !requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i))
679             return null;
680         this._parsedFormParameters = this._parseParameters(this.requestFormData);
681         return this._parsedFormParameters;
682     },
683
684     /**
685      * @return {string|undefined}
686      */
687     get responseHttpVersion()
688     {
689         var match = this.responseHeadersText.match(/^(HTTP\/\d+\.\d+)/);
690         return match ? match[1] : undefined;
691     },
692
693     /**
694      * @param {string} queryString
695      * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
696      */
697     _parseParameters: function(queryString)
698     {
699         function parseNameValue(pair)
700         {
701             var splitPair = pair.split("=", 2);
702             return {name: splitPair[0], value: splitPair[1] || ""};
703         }
704         return queryString.split("&").map(parseNameValue);
705     },
706
707     /**
708      * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
709      * @param {string} headerName
710      * @return {string|undefined}
711      */
712     _headerValue: function(headers, headerName)
713     {
714         headerName = headerName.toLowerCase();
715
716         var values = [];
717         for (var i = 0; i < headers.length; ++i) {
718             if (headers[i].name.toLowerCase() === headerName)
719                 values.push(headers[i].value);
720         }
721         if (!values.length)
722             return undefined;
723         // Set-Cookie values should be separated by '\n', not comma, otherwise cookies could not be parsed.
724         if (headerName === "set-cookie")
725             return values.join("\n");
726         return values.join(", ");
727     },
728
729     /**
730      * @return {?string|undefined}
731      */
732     get content()
733     {
734         return this._content;
735     },
736
737     /**
738      * @return {boolean}
739      */
740     get contentEncoded()
741     {
742         return this._contentEncoded;
743     },
744
745     /**
746      * @return {string}
747      */
748     contentURL: function()
749     {
750         return this._url;
751     },
752
753     /**
754      * @return {!WebInspector.ResourceType}
755      */
756     contentType: function()
757     {
758         return this._type;
759     },
760
761     /**
762      * @param {function(?string)} callback
763      */
764     requestContent: function(callback)
765     {
766         // We do not support content retrieval for WebSockets at the moment.
767         // Since WebSockets are potentially long-living, fail requests immediately
768         // to prevent caller blocking until resource is marked as finished.
769         if (this.type === WebInspector.resourceTypes.WebSocket) {
770             callback(null);
771             return;
772         }
773         if (typeof this._content !== "undefined") {
774             callback(this.content || null);
775             return;
776         }
777         this._pendingContentCallbacks.push(callback);
778         if (this.finished)
779             this._innerRequestContent();
780     },
781
782     /**
783      * @param {string} query
784      * @param {boolean} caseSensitive
785      * @param {boolean} isRegex
786      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
787      */
788     searchInContent: function(query, caseSensitive, isRegex, callback)
789     {
790         callback([]);
791     },
792
793     /**
794      * @return {boolean}
795      */
796     isHttpFamily: function()
797     {
798         return !!this.url.match(/^https?:/i);
799     },
800
801     /**
802      * @return {string|undefined}
803      */
804     requestContentType: function()
805     {
806         return this.requestHeaderValue("Content-Type");
807     },
808
809     /**
810      * @return {boolean}
811      */
812     isPingRequest: function()
813     {
814         return "text/ping" === this.requestContentType();
815     },
816
817     /**
818      * @return {boolean}
819      */
820     hasErrorStatusCode: function()
821     {
822         return this.statusCode >= 400;
823     },
824
825     /**
826      * @param {!Element} image
827      */
828     populateImageSource: function(image)
829     {
830         /**
831          * @this {WebInspector.NetworkRequest}
832          * @param {?string} content
833          */
834         function onResourceContent(content)
835         {
836             var imageSrc = this.asDataURL();
837             if (imageSrc === null)
838                 imageSrc = this.url;
839             image.src = imageSrc;
840         }
841
842         this.requestContent(onResourceContent.bind(this));
843     },
844
845     /**
846      * @return {?string}
847      */
848     asDataURL: function()
849     {
850         return WebInspector.contentAsDataURL(this._content, this.mimeType, this._contentEncoded);
851     },
852
853     _innerRequestContent: function()
854     {
855         if (this._contentRequested)
856             return;
857         this._contentRequested = true;
858
859         /**
860          * @param {?Protocol.Error} error
861          * @param {string} content
862          * @param {boolean} contentEncoded
863          * @this {WebInspector.NetworkRequest}
864          */
865         function onResourceContent(error, content, contentEncoded)
866         {
867             this._content = error ? null : content;
868             this._contentEncoded = contentEncoded;
869             var callbacks = this._pendingContentCallbacks.slice();
870             for (var i = 0; i < callbacks.length; ++i)
871                 callbacks[i](this._content);
872             this._pendingContentCallbacks.length = 0;
873             delete this._contentRequested;
874         }
875         NetworkAgent.getResponseBody(this._requestId, onResourceContent.bind(this));
876     },
877
878     /**
879      * @return {!{type: !WebInspector.NetworkRequest.InitiatorType, url: string, source: string, lineNumber: number, columnNumber: number}}
880      */
881     initiatorInfo: function()
882     {
883         if (this._initiatorInfo)
884             return this._initiatorInfo;
885
886         var type = WebInspector.NetworkRequest.InitiatorType.Other;
887         var url = "";
888         var lineNumber = -Infinity;
889         var columnNumber = -Infinity;
890
891         if (this.redirectSource) {
892             type = WebInspector.NetworkRequest.InitiatorType.Redirect;
893             url = this.redirectSource.url;
894         } else if (this.initiator) {
895             if (this.initiator.type === NetworkAgent.InitiatorType.Parser) {
896                 type = WebInspector.NetworkRequest.InitiatorType.Parser;
897                 url = this.initiator.url;
898                 lineNumber = this.initiator.lineNumber;
899             } else if (this.initiator.type === NetworkAgent.InitiatorType.Script) {
900                 var topFrame = this.initiator.stackTrace[0];
901                 if (topFrame.url) {
902                     type = WebInspector.NetworkRequest.InitiatorType.Script;
903                     url = topFrame.url;
904                     lineNumber = topFrame.lineNumber;
905                     columnNumber = topFrame.columnNumber;
906                 }
907             }
908         }
909
910         this._initiatorInfo = {type: type, url: url, source: WebInspector.displayNameForURL(url), lineNumber: lineNumber, columnNumber: columnNumber};
911         return this._initiatorInfo;
912     },
913
914     /**
915      * @return {!Array.<!Object>}
916      */
917     frames: function()
918     {
919         return this._frames;
920     },
921
922     /**
923      * @param {number} position
924      * @return {!Object|undefined}
925      */
926     frame: function(position)
927     {
928         return this._frames[position];
929     },
930
931     /**
932      * @param {string} errorMessage
933      * @param {number} time
934      */
935     addFrameError: function(errorMessage, time)
936     {
937         this._pushFrame({errorMessage: errorMessage, time: time});
938     },
939
940     /**
941      * @param {!NetworkAgent.WebSocketFrame} response
942      * @param {number} time
943      * @param {boolean} sent
944      */
945     addFrame: function(response, time, sent)
946     {
947         response.time = time;
948         if (sent)
949             response.sent = sent;
950         this._pushFrame(response);
951     },
952
953     /**
954      * @param {!Object} frameOrError
955      */
956     _pushFrame: function(frameOrError)
957     {
958         if (this._frames.length >= 100)
959             this._frames.splice(0, 10);
960         this._frames.push(frameOrError);
961     },
962
963     __proto__: WebInspector.Object.prototype
964 }