initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / CookieParser.js
1 /*
2  * Copyright (C) 2010 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 // Ideally, we would rely on platform support for parsing a cookie, since
32 // this would save us from any potential inconsistency. However, exposing
33 // platform cookie parsing logic would require quite a bit of additional
34 // plumbing, and at least some platforms lack support for parsing Cookie,
35 // which is in a format slightly different from Set-Cookie and is normally
36 // only required on the server side.
37
38 /**
39  * @constructor
40  */
41 WebInspector.CookieParser = function()
42 {
43 }
44
45 WebInspector.CookieParser.prototype = {
46     get cookies()
47     {
48         return this._cookies;
49     },
50
51     parseCookie: function(cookieHeader)
52     {
53         if (!this._initialize(cookieHeader))
54             return;
55
56         for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
57             if (kv.key.charAt(0) === "$" && this._lastCookie)
58                 this._lastCookie.addAttribute(kv.key.slice(1), kv.value);
59             else if (kv.key.toLowerCase() !== "$version" && typeof kv.value === "string")
60                 this._addCookie(kv, WebInspector.Cookie.Type.Request);
61             this._advanceAndCheckCookieDelimiter();
62         }
63         this._flushCookie();
64         return this._cookies;
65     },
66
67     parseSetCookie: function(setCookieHeader)
68     {
69         if (!this._initialize(setCookieHeader))
70             return;
71         for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
72             if (this._lastCookie)
73                 this._lastCookie.addAttribute(kv.key, kv.value);
74             else
75                 this._addCookie(kv, WebInspector.Cookie.Type.Response);
76             if (this._advanceAndCheckCookieDelimiter())
77                 this._flushCookie();
78         }
79         this._flushCookie();
80         return this._cookies;
81     },
82
83     _initialize: function(headerValue)
84     {
85         this._input = headerValue;
86         if (typeof headerValue !== "string")
87             return false;
88         this._cookies = [];
89         this._lastCookie = null;
90         this._originalInputLength = this._input.length;
91         return true;
92     },
93
94     _flushCookie: function()
95     {
96         if (this._lastCookie)
97             this._lastCookie.size = this._originalInputLength - this._input.length - this._lastCookiePosition;
98         this._lastCookie = null;
99     },
100
101     _extractKeyValue: function()
102     {
103         if (!this._input || !this._input.length)
104             return null;
105         // Note: RFCs offer an option for quoted values that may contain commas and semicolons.
106         // Many browsers/platforms do not support this, however (see http://webkit.org/b/16699
107         // and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox,
108         // Chrome and Safari on some old platforms. The latest version of Safari supports quoted
109         // cookie values, though.
110         var keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this._input);
111         if (!keyValueMatch) {
112             console.log("Failed parsing cookie header before: " + this._input);
113             return null;
114         }
115
116         var result = {
117             key: keyValueMatch[1],
118             value: keyValueMatch[2] && keyValueMatch[2].trim(),
119             position: this._originalInputLength - this._input.length
120         };
121         this._input = this._input.slice(keyValueMatch[0].length);
122         return result;
123     },
124
125     _advanceAndCheckCookieDelimiter: function()
126     {
127         var match = /^\s*[\n;]\s*/.exec(this._input);
128         if (!match)
129             return false;
130         this._input = this._input.slice(match[0].length);
131         return match[0].match("\n") !== null;
132     },
133
134     _addCookie: function(keyValue, type)
135     {
136         if (this._lastCookie)
137             this._lastCookie.size = keyValue.position - this._lastCookiePosition;
138         // Mozilla bug 169091: Mozilla, IE and Chrome treat single token (w/o "=") as
139         // specifying a value for a cookie with empty name.
140         this._lastCookie = keyValue.value ? new WebInspector.Cookie(keyValue.key, keyValue.value, type) :
141             new WebInspector.Cookie("", keyValue.key, type);
142         this._lastCookiePosition = keyValue.position;
143         this._cookies.push(this._lastCookie);
144     }
145 };
146
147 WebInspector.CookieParser.parseCookie = function(header)
148 {
149     return (new WebInspector.CookieParser()).parseCookie(header);
150 }
151
152 WebInspector.CookieParser.parseSetCookie = function(header)
153 {
154     return (new WebInspector.CookieParser()).parseSetCookie(header);
155 }
156
157 /**
158  * @constructor
159  */
160 WebInspector.Cookie = function(name, value, type)
161 {
162     this.name = name;
163     this.value = value;
164     this.type = type;
165     this._attributes = {};
166 }
167
168 WebInspector.Cookie.prototype = {
169     get httpOnly()
170     {
171         return "httponly" in this._attributes;
172     },
173
174     get secure()
175     {
176         return "secure" in this._attributes;
177     },
178
179     get session()
180     {
181         // RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used.
182         // Check for absence of explicity max-age or expiry date instead.
183         return  !("expries" in this._attributes || "max-age" in this._attributes);
184     },
185
186     get path()
187     {
188         return this._attributes.path;
189     },
190
191     get domain()
192     {
193         return this._attributes.domain;
194     },
195
196     expires: function(requestDate)
197     {
198         return this._attributes.expires ? new Date(this._attributes.expires) :
199             (this._attributes["max-age"] ? new Date(requestDate.getTime() + 1000 * this._attributes["max-age"]) : null);
200     },
201
202     get attributes()
203     {
204         return this._attributes;
205     },
206
207     addAttribute: function(key, value)
208     {
209         this._attributes[key.toLowerCase()] = value;
210     }
211 }
212
213 WebInspector.Cookie.Type = {
214     Request: 0,
215     Response: 1
216 };