Home | History | Annotate | Download | only in front-end
      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 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 
    184 WebInspector.HAREntry._toMilliseconds = function(time)
    185 {
    186     return time === -1 ? -1 : Math.round(time * 1000);
    187 }
    188 
    189 WebInspector.HARLog = function()
    190 {
    191 }
    192 
    193 WebInspector.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