Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / HAREntry.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 // See http://www.softwareishard.com/blog/har-12-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 /**
38  * @constructor
39  * @param {!WebInspector.NetworkRequest} request
40  */
41 WebInspector.HAREntry = function(request)
42 {
43     this._request = request;
44 }
45
46 WebInspector.HAREntry.prototype = {
47     /**
48      * @return {!Object}
49      */
50     build: function()
51     {
52         var entry = {
53             startedDateTime: new Date(this._request.startTime * 1000),
54             time: this._request.timing ? WebInspector.HAREntry._toMilliseconds(this._request.duration) : 0,
55             request: this._buildRequest(),
56             response: this._buildResponse(),
57             cache: { }, // Not supported yet.
58             timings: this._buildTimings()
59         };
60
61         if (this._request.connectionId !== "0")
62             entry.connection = this._request.connectionId;
63         var page = this._request.target().networkLog.pageLoadForRequest(this._request);
64         if (page)
65             entry.pageref = "page_" + page.id;
66         return entry;
67     },
68
69     /**
70      * @return {!Object}
71      */
72     _buildRequest: function()
73     {
74         var headersText = this._request.requestHeadersText();
75         var res = {
76             method: this._request.requestMethod,
77             url: this._buildRequestURL(this._request.url),
78             httpVersion: this._request.requestHttpVersion(),
79             headers: this._request.requestHeaders(),
80             queryString: this._buildParameters(this._request.queryParameters || []),
81             cookies: this._buildCookies(this._request.requestCookies || []),
82             headersSize: headersText ? headersText.length : -1,
83             bodySize: this.requestBodySize
84         };
85         if (this._request.requestFormData)
86             res.postData = this._buildPostData();
87
88         return res;
89     },
90
91     /**
92      * @return {!Object}
93      */
94     _buildResponse: function()
95     {
96         var headersText = this._request.responseHeadersText;
97         return {
98             status: this._request.statusCode,
99             statusText: this._request.statusText,
100             httpVersion: this._request.responseHttpVersion(),
101             headers: this._request.responseHeaders,
102             cookies: this._buildCookies(this._request.responseCookies || []),
103             content: this._buildContent(),
104             redirectURL: this._request.responseHeaderValue("Location") || "",
105             headersSize: headersText ? headersText.length : -1,
106             bodySize: this.responseBodySize,
107             _error: this._request.localizedFailDescription
108         };
109     },
110
111     /**
112      * @return {!Object}
113      */
114     _buildContent: function()
115     {
116         var content = {
117             size: this._request.resourceSize,
118             mimeType: this._request.mimeType || "x-unknown",
119             // text: this._request.content // TODO: pull out into a boolean flag, as content can be huge (and needs to be requested with an async call)
120         };
121         var compression = this.responseCompression;
122         if (typeof compression === "number")
123             content.compression = compression;
124         return content;
125     },
126
127     /**
128      * @return {!Object}
129      */
130     _buildTimings: function()
131     {
132         // Order of events: request_start = 0, [proxy], [dns], [connect [ssl]], [send], receive_headers_end
133         // HAR 'blocked' time is time before first network activity.
134
135         var timing = this._request.timing;
136         if (!timing)
137             return {blocked: -1, dns: -1, connect: -1, send: 0, wait: 0, receive: 0, ssl: -1};
138
139         function firstNonNegative(values)
140         {
141             for (var i = 0; i < values.length; ++i) {
142                 if (values[i] >= 0)
143                     return values[i];
144             }
145             console.assert(false, "Incomplete requet timing information.");
146         }
147
148         var blocked = firstNonNegative([timing.dnsStart, timing.connectStart, timing.sendStart]);
149
150         var dns = -1;
151         if (timing.dnsStart >= 0)
152             dns = firstNonNegative([timing.connectStart, timing.sendStart]) - timing.dnsStart;
153
154         var connect = -1;
155         if (timing.connectStart >= 0)
156             connect = timing.sendStart - timing.connectStart;
157
158         var send = timing.sendEnd - timing.sendStart;
159         var wait = timing.receiveHeadersEnd - timing.sendEnd;
160         var receive = WebInspector.HAREntry._toMilliseconds(this._request.duration) - timing.receiveHeadersEnd;
161
162         var ssl = -1;
163         if (timing.sslStart >= 0 && timing.sslEnd >= 0)
164             ssl = timing.sslEnd - timing.sslStart;
165
166         return {blocked: blocked, dns: dns, connect: connect, send: send, wait: wait, receive: receive, ssl: ssl};
167     },
168
169     /**
170      * @return {!Object}
171      */
172     _buildPostData: function()
173     {
174         var res = {
175             mimeType: this._request.requestContentType(),
176             text: this._request.requestFormData
177         };
178         if (this._request.formParameters)
179             res.params = this._buildParameters(this._request.formParameters);
180         return res;
181     },
182
183     /**
184      * @param {!Array.<!Object>} parameters
185      * @return {!Array.<!Object>}
186      */
187     _buildParameters: function(parameters)
188     {
189         return parameters.slice();
190     },
191
192     /**
193      * @param {string} url
194      * @return {string}
195      */
196     _buildRequestURL: function(url)
197     {
198         return url.split("#", 2)[0];
199     },
200
201     /**
202      * @param {!Array.<!WebInspector.Cookie>} cookies
203      * @return {!Array.<!Object>}
204      */
205     _buildCookies: function(cookies)
206     {
207         return cookies.map(this._buildCookie.bind(this));
208     },
209
210     /**
211      * @param {!WebInspector.Cookie} cookie
212      * @return {!Object}
213      */
214     _buildCookie: function(cookie)
215     {
216         return {
217             name: cookie.name(),
218             value: cookie.value(),
219             path: cookie.path(),
220             domain: cookie.domain(),
221             expires: cookie.expiresDate(new Date(this._request.startTime * 1000)),
222             httpOnly: cookie.httpOnly(),
223             secure: cookie.secure()
224         };
225     },
226
227     /**
228      * @return {number}
229      */
230     get requestBodySize()
231     {
232         return !this._request.requestFormData ? 0 : this._request.requestFormData.length;
233     },
234
235     /**
236      * @return {number}
237      */
238     get responseBodySize()
239     {
240         if (this._request.cached() || this._request.statusCode === 304)
241             return 0;
242         if (!this._request.responseHeadersText)
243             return -1;
244         return this._request.transferSize - this._request.responseHeadersText.length;
245     },
246
247     /**
248      * @return {number|undefined}
249      */
250     get responseCompression()
251     {
252         if (this._request.cached() || this._request.statusCode === 304 || this._request.statusCode === 206)
253             return;
254         if (!this._request.responseHeadersText)
255             return;
256         return this._request.resourceSize - this.responseBodySize;
257     }
258 }
259
260 /**
261  * @param {number} time
262  * @return {number}
263  */
264 WebInspector.HAREntry._toMilliseconds = function(time)
265 {
266     return time === -1 ? -1 : time * 1000;
267 }
268
269 /**
270  * @constructor
271  * @param {!Array.<!WebInspector.NetworkRequest>} requests
272  */
273 WebInspector.HARLog = function(requests)
274 {
275     this._requests = requests;
276 }
277
278 WebInspector.HARLog.prototype = {
279     /**
280      * @return {!Object}
281      */
282     build: function()
283     {
284         return {
285             version: "1.2",
286             creator: this._creator(),
287             pages: this._buildPages(),
288             entries: this._requests.map(this._convertResource.bind(this))
289         }
290     },
291
292     _creator: function()
293     {
294         var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
295
296         return {
297             name: "WebInspector",
298             version: webKitVersion ? webKitVersion[1] : "n/a"
299         };
300     },
301
302     /**
303      * @return {!Array.<!Object>}
304      */
305     _buildPages: function()
306     {
307         var seenIdentifiers = {};
308         var pages = [];
309         for (var i = 0; i < this._requests.length; ++i) {
310             var page = this._requests[i].target().networkLog.pageLoadForRequest(this._requests[i]);
311             if (!page || seenIdentifiers[page.id])
312                 continue;
313             seenIdentifiers[page.id] = true;
314             pages.push(this._convertPage(page));
315         }
316         return pages;
317     },
318
319     /**
320      * @param {!WebInspector.PageLoad} page
321      * @return {!Object}
322      */
323     _convertPage: function(page)
324     {
325         return {
326             startedDateTime: new Date(page.startTime * 1000),
327             id: "page_" + page.id,
328             title: page.url, // We don't have actual page title here. URL is probably better than nothing.
329             pageTimings: {
330                 onContentLoad: this._pageEventTime(page, page.contentLoadTime),
331                 onLoad: this._pageEventTime(page, page.loadTime)
332             }
333         }
334     },
335
336     /**
337      * @param {!WebInspector.NetworkRequest} request
338      * @return {!Object}
339      */
340     _convertResource: function(request)
341     {
342         return (new WebInspector.HAREntry(request)).build();
343     },
344
345     /**
346      * @param {!WebInspector.PageLoad} page
347      * @param {number} time
348      * @return {number}
349      */
350     _pageEventTime: function(page, time)
351     {
352         var startTime = page.startTime;
353         if (time === -1 || startTime === -1)
354             return -1;
355         return WebInspector.HAREntry._toMilliseconds(time - startTime);
356     }
357 }