• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
37WebInspector.HAREntry = function(resource)
38{
39    this._resource = resource;
40}
41
42WebInspector.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 supproted yet.
52            timings: this._buildTimings()
53        };
54    },
55
56    _buildRequest: function()
57    {
58        var res = {
59            method: this._resource.requestMethod,
60            url: this._resource.url,
61            // httpVersion: "HTTP/1.1" -- Not available.
62            headers: this._buildHeaders(this._resource.requestHeaders),
63            queryString: this._buildParameters(this._resource.queryParameters || []),
64            cookies: this._buildCookies(this._resource.requestCookies || []),
65            headersSize: -1, // Not available.
66            bodySize: -1 // Not available.
67        };
68        if (this._resource.requestFormData)
69            res.postData = this._buildPostData();
70        return res;
71    },
72
73    _buildResponse: function()
74    {
75        return {
76            status: this._resource.statusCode,
77            statusText: this._resource.statusText,
78            // "httpVersion": "HTTP/1.1" -- Not available.
79            headers: this._buildHeaders(this._resource.responseHeaders),
80            cookies: this._buildCookies(this._resource.responseCookies || []),
81            content: this._buildContent(),
82            redirectURL: this._resource.responseHeaderValue("Location") || "",
83            headersSize: -1, // Not available.
84            bodySize: this._resource.resourceSize
85        };
86    },
87
88    _buildContent: function()
89    {
90        return {
91            size: this._resource.resourceSize,
92            // compression: 0, -- Not available.
93            mimeType: this._resource.mimeType,
94            // text: -- Not available.
95        };
96    },
97
98    _buildTimings: function()
99    {
100        var waitForConnection = this._interval("connectStart", "connectEnd");
101        var blocked;
102        var connect;
103        var dns = this._interval("dnsStart", "dnsEnd");
104        var send = this._interval("sendStart", "sendEnd");
105        var ssl = this._interval("sslStart", "sslEnd");
106
107        if (ssl !== -1 && send !== -1)
108            send -= ssl;
109
110        if (this._resource.connectionReused) {
111            connect = -1;
112            blocked = waitForConnection;
113        } else {
114            blocked = 0;
115            connect = waitForConnection;
116            if (dns !== -1)
117                connect -= dns;
118        }
119
120        return {
121            blocked: blocked,
122            dns: dns,
123            connect: connect,
124            send: send,
125            wait: this._interval("sendEnd", "receiveHeadersEnd"),
126            receive: WebInspector.HAREntry._toMilliseconds(this._resource.receiveDuration),
127            ssl: ssl
128        };
129    },
130
131    _buildHeaders: function(headers)
132    {
133        var result = [];
134        for (var name in headers)
135            result.push({ name: name, value: headers[name] });
136        return result;
137    },
138
139    _buildPostData: function()
140    {
141        var res = {
142            mimeType: this._resource.requestHeaderValue("Content-Type"),
143            text: this._resource.requestFormData
144        };
145        if (this._resource.formParameters)
146           res.params = this._buildParameters(this._resource.formParameters);
147        return res;
148    },
149
150    _buildParameters: function(parameters)
151    {
152        return parameters.slice();
153    },
154
155    _buildCookies: function(cookies)
156    {
157        return cookies.map(this._buildCookie.bind(this));
158    },
159
160    _buildCookie: function(cookie)
161    {
162
163        return {
164            name: cookie.name,
165            value: cookie.value,
166            path: cookie.path,
167            domain: cookie.domain,
168            expires: cookie.expires(new Date(this._resource.startTime * 1000)),
169            httpOnly: cookie.httpOnly,
170            secure: cookie.secure
171        };
172    },
173
174    _interval: function(start, end)
175    {
176        var timing = this._resource.timing;
177        if (!timing)
178            return -1;
179        var startTime = timing[start];
180        return typeof startTime !== "number" || startTime === -1 ? -1 : Math.round(timing[end] - startTime);
181    }
182}
183
184WebInspector.HAREntry._toMilliseconds = function(time)
185{
186    return time === -1 ? -1 : Math.round(time * 1000);
187}
188
189WebInspector.HARLog = function()
190{
191}
192
193WebInspector.HARLog.prototype = {
194    build: function()
195    {
196        var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
197
198        return {
199            version: "1.2",
200            creator: {
201                name: "WebInspector",
202                version: webKitVersion ? webKitVersion[1] : "n/a"
203            },
204            pages: this._buildPages(),
205            entries: WebInspector.networkResources.map(this._convertResource.bind(this))
206        }
207    },
208
209    _buildPages: function()
210    {
211        return [
212            {
213                startedDateTime: new Date(WebInspector.mainResource.startTime * 1000),
214                id: WebInspector.mainResource.documentURL,
215                title: "",
216                pageTimings: this.buildMainResourceTimings()
217            }
218        ];
219    },
220
221    buildMainResourceTimings: function()
222    {
223        return {
224             onContentLoad: this._pageEventTime(WebInspector.mainResourceDOMContentTime),
225             onLoad: this._pageEventTime(WebInspector.mainResourceLoadTime),
226        }
227    },
228
229    _convertResource: function(resource)
230    {
231        return (new WebInspector.HAREntry(resource)).build();
232    },
233
234    _pageEventTime: function(time)
235    {
236        var startTime = WebInspector.mainResource.startTime;
237        if (time === -1 || startTime === -1)
238            return -1;
239        return WebInspector.HAREntry._toMilliseconds(time - startTime);
240    }
241}
242