diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/sdk/CookieParser.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/sdk/CookieParser.js | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CookieParser.js b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CookieParser.js new file mode 100644 index 00000000000..26110e4552d --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CookieParser.js @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Ideally, we would rely on platform support for parsing a cookie, since +// this would save us from any potential inconsistency. However, exposing +// platform cookie parsing logic would require quite a bit of additional +// plumbing, and at least some platforms lack support for parsing Cookie, +// which is in a format slightly different from Set-Cookie and is normally +// only required on the server side. + +/** + * @constructor + */ +WebInspector.CookieParser = function() +{ +} + +/** + * @constructor + * @param {string} key + * @param {string|undefined} value + * @param {number} position + */ +WebInspector.CookieParser.KeyValue = function(key, value, position) +{ + this.key = key; + this.value = value; + this.position = position; +} + +WebInspector.CookieParser.prototype = { + /** + * @return {!Array.<!WebInspector.Cookie>} + */ + cookies: function() + { + return this._cookies; + }, + + /** + * @param {string|undefined} cookieHeader + * @return {?Array.<!WebInspector.Cookie>} + */ + parseCookie: function(cookieHeader) + { + if (!this._initialize(cookieHeader)) + return null; + + for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) { + if (kv.key.charAt(0) === "$" && this._lastCookie) + this._lastCookie.addAttribute(kv.key.slice(1), kv.value); + else if (kv.key.toLowerCase() !== "$version" && typeof kv.value === "string") + this._addCookie(kv, WebInspector.Cookie.Type.Request); + this._advanceAndCheckCookieDelimiter(); + } + this._flushCookie(); + return this._cookies; + }, + + /** + * @param {string|undefined} setCookieHeader + * @return {?Array.<!WebInspector.Cookie>} + */ + parseSetCookie: function(setCookieHeader) + { + if (!this._initialize(setCookieHeader)) + return null; + for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) { + if (this._lastCookie) + this._lastCookie.addAttribute(kv.key, kv.value); + else + this._addCookie(kv, WebInspector.Cookie.Type.Response); + if (this._advanceAndCheckCookieDelimiter()) + this._flushCookie(); + } + this._flushCookie(); + return this._cookies; + }, + + /** + * @param {string|undefined} headerValue + * @return {boolean} + */ + _initialize: function(headerValue) + { + this._input = headerValue; + if (typeof headerValue !== "string") + return false; + this._cookies = []; + this._lastCookie = null; + this._originalInputLength = this._input.length; + return true; + }, + + _flushCookie: function() + { + if (this._lastCookie) + this._lastCookie.setSize(this._originalInputLength - this._input.length - this._lastCookiePosition); + this._lastCookie = null; + }, + + /** + * @return {?WebInspector.CookieParser.KeyValue} + */ + _extractKeyValue: function() + { + if (!this._input || !this._input.length) + return null; + // Note: RFCs offer an option for quoted values that may contain commas and semicolons. + // Many browsers/platforms do not support this, however (see http://webkit.org/b/16699 + // and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox, + // Chrome and Safari on some old platforms. The latest version of Safari supports quoted + // cookie values, though. + var keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this._input); + if (!keyValueMatch) { + console.log("Failed parsing cookie header before: " + this._input); + return null; + } + + var result = new WebInspector.CookieParser.KeyValue(keyValueMatch[1], keyValueMatch[2] && keyValueMatch[2].trim(), this._originalInputLength - this._input.length); + this._input = this._input.slice(keyValueMatch[0].length); + return result; + }, + + /** + * @return {boolean} + */ + _advanceAndCheckCookieDelimiter: function() + { + var match = /^\s*[\n;]\s*/.exec(this._input); + if (!match) + return false; + this._input = this._input.slice(match[0].length); + return match[0].match("\n") !== null; + }, + + /** + * @param {!WebInspector.CookieParser.KeyValue} keyValue + * @param {!WebInspector.Cookie.Type} type + */ + _addCookie: function(keyValue, type) + { + if (this._lastCookie) + this._lastCookie.setSize(keyValue.position - this._lastCookiePosition); + // Mozilla bug 169091: Mozilla, IE and Chrome treat single token (w/o "=") as + // specifying a value for a cookie with empty name. + this._lastCookie = typeof keyValue.value === "string" ? new WebInspector.Cookie(keyValue.key, keyValue.value, type) : + new WebInspector.Cookie("", keyValue.key, type); + this._lastCookiePosition = keyValue.position; + this._cookies.push(this._lastCookie); + } +}; + +/** + * @param {string|undefined} header + * @return {?Array.<!WebInspector.Cookie>} + */ +WebInspector.CookieParser.parseCookie = function(header) +{ + return (new WebInspector.CookieParser()).parseCookie(header); +} + +/** + * @param {string|undefined} header + * @return {?Array.<!WebInspector.Cookie>} + */ +WebInspector.CookieParser.parseSetCookie = function(header) +{ + return (new WebInspector.CookieParser()).parseSetCookie(header); +} + +/** + * @constructor + * @param {string} name + * @param {string} value + * @param {?WebInspector.Cookie.Type} type + */ +WebInspector.Cookie = function(name, value, type) +{ + this._name = name; + this._value = value; + this._type = type; + this._attributes = {}; +} + +WebInspector.Cookie.prototype = { + /** + * @return {string} + */ + name: function() + { + return this._name; + }, + + /** + * @return {string} + */ + value: function() + { + return this._value; + }, + + /** + * @return {?WebInspector.Cookie.Type} + */ + type: function() + { + return this._type; + }, + + /** + * @return {boolean} + */ + httpOnly: function() + { + return "httponly" in this._attributes; + }, + + /** + * @return {boolean} + */ + secure: function() + { + return "secure" in this._attributes; + }, + + /** + * @return {boolean} + */ + session: function() + { + // RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used. + // Check for absence of explicitly max-age or expiry date instead. + return !("expires" in this._attributes || "max-age" in this._attributes); + }, + + /** + * @return {string} + */ + path: function() + { + return this._attributes["path"]; + }, + + /** + * @return {string} + */ + port: function() + { + return this._attributes["port"]; + }, + + /** + * @return {string} + */ + domain: function() + { + return this._attributes["domain"]; + }, + + /** + * @return {string} + */ + expires: function() + { + return this._attributes["expires"]; + }, + + /** + * @return {string} + */ + maxAge: function() + { + return this._attributes["max-age"]; + }, + + /** + * @return {number} + */ + size: function() + { + return this._size; + }, + + /** + * @param {number} size + */ + setSize: function(size) + { + this._size = size; + }, + + /** + * @return {?Date} + */ + expiresDate: function(requestDate) + { + // RFC 6265 indicates that the max-age attribute takes precedence over the expires attribute + if (this.maxAge()) { + var targetDate = requestDate === null ? new Date() : requestDate; + return new Date(targetDate.getTime() + 1000 * this.maxAge()); + } + + if (this.expires()) + return new Date(this.expires()); + + return null; + }, + + /** + * @return {!Object} + */ + attributes: function() + { + return this._attributes; + }, + + /** + * @param {string} key + * @param {string=} value + */ + addAttribute: function(key, value) + { + this._attributes[key.toLowerCase()] = value; + }, + + /** + * @param {function(?Protocol.Error)=} callback + */ + remove: function(callback) + { + PageAgent.deleteCookie(this.name(), (this.secure() ? "https://" : "http://") + this.domain() + this.path(), callback); + } +} + +/** + * @enum {number} + */ +WebInspector.Cookie.Type = { + Request: 0, + Response: 1 +}; + +WebInspector.Cookies = {} + +/** + * @param {function(!Array.<!WebInspector.Cookie>)} callback + */ +WebInspector.Cookies.getCookiesAsync = function(callback) +{ + /** + * @param {?Protocol.Error} error + * @param {!Array.<!PageAgent.Cookie>} cookies + */ + function mycallback(error, cookies) + { + if (error) + return; + callback(cookies.map(WebInspector.Cookies.buildCookieProtocolObject)); + } + + PageAgent.getCookies(mycallback); +} + +/** + * @param {!PageAgent.Cookie} protocolCookie + * @return {!WebInspector.Cookie} + */ +WebInspector.Cookies.buildCookieProtocolObject = function(protocolCookie) +{ + var cookie = new WebInspector.Cookie(protocolCookie.name, protocolCookie.value, null); + cookie.addAttribute("domain", protocolCookie["domain"]); + cookie.addAttribute("path", protocolCookie["path"]); + cookie.addAttribute("port", protocolCookie["port"]); + if (protocolCookie["expires"]) + cookie.addAttribute("expires", protocolCookie["expires"]); + if (protocolCookie["httpOnly"]) + cookie.addAttribute("httpOnly"); + if (protocolCookie["secure"]) + cookie.addAttribute("secure"); + cookie.setSize(protocolCookie["size"]); + return cookie; +} + +/** + * @param {!WebInspector.Cookie} cookie + * @param {string} resourceURL + * @return {boolean} + */ +WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL) +{ + var url = resourceURL.asParsedURL(); + if (!url || !WebInspector.Cookies.cookieDomainMatchesResourceDomain(cookie.domain(), url.host)) + return false; + return (url.path.startsWith(cookie.path()) + && (!cookie.port() || url.port == cookie.port()) + && (!cookie.secure() || url.scheme === "https")); +} + +/** + * @param {string} cookieDomain + * @param {string} resourceDomain + * @return {boolean} + */ +WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain) +{ + if (cookieDomain.charAt(0) !== '.') + return resourceDomain === cookieDomain; + return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)*" + cookieDomain.substring(1).escapeForRegExp() + "$", "i")); +} |