initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / HAREntry.js
1 /*
2  * Copyright (C) 2011 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 // See http://groups.google.com/group/http-archive-specification/web/har-1-2-spec
32 // for HAR specification.
33
34 // FIXME: Some fields are not yet supported due to back-end limitations.
35 // See https://bugs.webkit.org/show_bug.cgi?id=58127 for details.
36
37 WebInspector.HAREntry = function(resource)
38 {
39     this._resource = resource;
40 }
41
42 WebInspector.HAREntry.prototype = {
43     build: function()
44     {
45         return {
46             pageref: this._resource.documentURL,
47             startedDateTime: new Date(this._resource.startTime * 1000),
48             time: WebInspector.HAREntry._toMilliseconds(this._resource.duration),
49             request: this._buildRequest(),
50             response: this._buildResponse(),
51             cache: { }, // Not supported yet.
52             timings: this._buildTimings()
53         };
54     },
55
56     _buildRequest: function()
57     {
58         var res = {
59             method: this._resource.requestMethod,
60             url: this._buildRequestURL(this._resource.url),
61             httpVersion: this._resource.requestHttpVersion,
62             headers: this._buildHeaders(this._resource.requestHeaders),
63             queryString: this._buildParameters(this._resource.queryParameters || []),
64             cookies: this._buildCookies(this._resource.requestCookies || []),
65             headersSize: this._resource.requestHeadersSize,
66             bodySize: this.requestBodySize
67         };
68         if (this._resource.requestFormData)
69             res.postData = this._buildPostData();
70
71         return res;
72     },
73
74     _buildResponse: function()
75     {
76         return {
77             status: this._resource.statusCode,
78             statusText: this._resource.statusText,
79             httpVersion: this._resource.responseHttpVersion,
80             headers: this._buildHeaders(this._resource.responseHeaders),
81             cookies: this._buildCookies(this._resource.responseCookies || []),
82             content: this._buildContent(),
83             redirectURL: this._resource.responseHeaderValue("Location") || "",
84             headersSize: this._resource.responseHeadersSize,
85             bodySize: this.responseBodySize
86         };
87     },
88
89     _buildContent: function()
90     {
91         return {
92             size: this._resource.resourceSize,
93             compression: this.responseCompression,
94             mimeType: this._resource.mimeType,
95             // text: this._resource.content // TODO: pull out into a boolean flag, as content can be huge (and needs to be requested with an async call)
96         };
97     },
98
99     _buildTimings: function()
100     {
101         var waitForConnection = this._interval("connectStart", "connectEnd");
102         var blocked;
103         var connect;
104         var dns = this._interval("dnsStart", "dnsEnd");
105         var send = this._interval("sendStart", "sendEnd");
106         var ssl = this._interval("sslStart", "sslEnd");
107
108         if (ssl !== -1 && send !== -1)
109             send -= ssl;
110
111         if (this._resource.connectionReused) {
112             connect = -1;
113             blocked = waitForConnection;
114         } else {
115             blocked = 0;
116             connect = waitForConnection;
117             if (dns !== -1)
118                 connect -= dns;
119         }
120
121         return {
122             blocked: blocked,
123             dns: dns,
124             connect: connect,
125             send: send,
126             wait: this._interval("sendEnd", "receiveHeadersEnd"),
127             receive: WebInspector.HAREntry._toMilliseconds(this._resource.receiveDuration),
128             ssl: ssl
129         };
130     },
131
132     _buildHeaders: function(headers)
133     {
134         var result = [];
135         for (var name in headers)
136             result.push({ name: name, value: headers[name] });
137         return result;
138     },
139
140     _buildPostData: function()
141     {
142         var res = {
143             mimeType: this._resource.requestHeaderValue("Content-Type"),
144             text: this._resource.requestFormData
145         };
146         if (this._resource.formParameters)
147            res.params = this._buildParameters(this._resource.formParameters);
148         return res;
149     },
150
151     _buildParameters: function(parameters)
152     {
153         return parameters.slice();
154     },
155
156     _buildRequestURL: function(url)
157     {
158         return url.split("#", 2)[0];
159     },
160
161     _buildCookies: function(cookies)
162     {
163         return cookies.map(this._buildCookie.bind(this));
164     },
165
166     _buildCookie: function(cookie)
167     {
168         return {
169             name: cookie.name,
170             value: cookie.value,
171             path: cookie.path,
172             domain: cookie.domain,
173             expires: cookie.expires(new Date(this._resource.startTime * 1000)),
174             httpOnly: cookie.httpOnly,
175             secure: cookie.secure
176         };
177     },
178
179     _interval: function(start, end)
180     {
181         var timing = this._resource.timing;
182         if (!timing)
183             return -1;
184         var startTime = timing[start];
185         return typeof startTime !== "number" || startTime === -1 ? -1 : Math.round(timing[end] - startTime);
186     },
187
188     get requestBodySize()
189     {
190         return !this._resource.requestFormData ? 0 : this._resource.requestFormData.length;
191     },
192
193     get responseBodySize()
194     {
195         return this._resource.transferSize - this._resource.responseHeadersSize
196     },
197
198     get responseCompression()
199     {
200         return this._resource.resourceSize - (this._resource.transferSize - this._resource.responseHeadersSize);
201     }
202 }
203
204 WebInspector.HAREntry._toMilliseconds = function(time)
205 {
206     return time === -1 ? -1 : Math.round(time * 1000);
207 }
208
209 WebInspector.HARLog = function(resources)
210 {
211     this._resources = resources;
212 }
213
214 WebInspector.HARLog.prototype = {
215     build: function()
216     {
217         var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
218
219         return {
220             version: "1.2",
221             creator: {
222                 name: "WebInspector",
223                 version: webKitVersion ? webKitVersion[1] : "n/a"
224             },
225             pages: this._buildPages(),
226             entries: this._resources.map(this._convertResource.bind(this))
227         }
228     },
229
230     _buildPages: function()
231     {
232         return [
233             {
234                 startedDateTime: new Date(WebInspector.mainResource.startTime * 1000),
235                 id: WebInspector.mainResource.documentURL,
236                 title: "",
237                 pageTimings: this.buildMainResourceTimings()
238             }
239         ];
240     },
241
242     buildMainResourceTimings: function()
243     {
244         return {
245              onContentLoad: this._pageEventTime(WebInspector.mainResourceDOMContentTime),
246              onLoad: this._pageEventTime(WebInspector.mainResourceLoadTime),
247         }
248     },
249
250     _convertResource: function(resource)
251     {
252         return (new WebInspector.HAREntry(resource)).build();
253     },
254
255     _pageEventTime: function(time)
256     {
257         var startTime = WebInspector.mainResource.startTime;
258         if (time === -1 || startTime === -1)
259             return -1;
260         return WebInspector.HAREntry._toMilliseconds(time - startTime);
261     }
262 }