cookies.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Functions for setting, getting and deleting cookies.
  16. *
  17. * @author arv@google.com (Erik Arvidsson)
  18. */
  19. goog.provide('goog.net.Cookies');
  20. goog.provide('goog.net.cookies');
  21. goog.require('goog.string');
  22. /**
  23. * A class for handling browser cookies.
  24. * @param {?Document} context The context document to get/set cookies on.
  25. * @constructor
  26. * @final
  27. */
  28. goog.net.Cookies = function(context) {
  29. /**
  30. * The context document to get/set cookies on. If no document context is
  31. * passed, use a fake one with only the "cookie" attribute. This allows
  32. * this class to be instantiated safely in web worker environments.
  33. * @private {{cookie: string}}
  34. */
  35. this.document_ = context || {cookie: ''};
  36. };
  37. /**
  38. * Static constant for the size of cookies. Per the spec, there's a 4K limit
  39. * to the size of a cookie. To make sure users can't break this limit, we
  40. * should truncate long cookies at 3950 bytes, to be extra careful with dumb
  41. * browsers/proxies that interpret 4K as 4000 rather than 4096.
  42. * @type {number}
  43. */
  44. goog.net.Cookies.MAX_COOKIE_LENGTH = 3950;
  45. /**
  46. * Returns true if cookies are enabled.
  47. * @return {boolean} True if cookies are enabled.
  48. */
  49. goog.net.Cookies.prototype.isEnabled = function() {
  50. return navigator.cookieEnabled;
  51. };
  52. /**
  53. * We do not allow '=', ';', or white space in the name.
  54. *
  55. * NOTE: The following are allowed by this method, but should be avoided for
  56. * cookies handled by the server.
  57. * - any name starting with '$'
  58. * - 'Comment'
  59. * - 'Domain'
  60. * - 'Expires'
  61. * - 'Max-Age'
  62. * - 'Path'
  63. * - 'Secure'
  64. * - 'Version'
  65. *
  66. * @param {string} name Cookie name.
  67. * @return {boolean} Whether name is valid.
  68. *
  69. * @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
  70. * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
  71. */
  72. goog.net.Cookies.prototype.isValidName = function(name) {
  73. return !(/[;=\s]/.test(name));
  74. };
  75. /**
  76. * We do not allow ';' or line break in the value.
  77. *
  78. * Spec does not mention any illegal characters, but in practice semi-colons
  79. * break parsing and line breaks truncate the name.
  80. *
  81. * @param {string} value Cookie value.
  82. * @return {boolean} Whether value is valid.
  83. *
  84. * @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
  85. * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
  86. */
  87. goog.net.Cookies.prototype.isValidValue = function(value) {
  88. return !(/[;\r\n]/.test(value));
  89. };
  90. /**
  91. * Sets a cookie. The max_age can be -1 to set a session cookie. To remove and
  92. * expire cookies, use remove() instead.
  93. *
  94. * Neither the {@code name} nor the {@code value} are encoded in any way. It is
  95. * up to the callers of {@code get} and {@code set} (as well as all the other
  96. * methods) to handle any possible encoding and decoding.
  97. *
  98. * @throws {!Error} If the {@code name} fails #goog.net.cookies.isValidName.
  99. * @throws {!Error} If the {@code value} fails #goog.net.cookies.isValidValue.
  100. *
  101. * @param {string} name The cookie name.
  102. * @param {string} value The cookie value.
  103. * @param {number=} opt_maxAge The max age in seconds (from now). Use -1 to
  104. * set a session cookie. If not provided, the default is -1
  105. * (i.e. set a session cookie).
  106. * @param {?string=} opt_path The path of the cookie. If not present then this
  107. * uses the full request path.
  108. * @param {?string=} opt_domain The domain of the cookie, or null to not
  109. * specify a domain attribute (browser will use the full request host name).
  110. * If not provided, the default is null (i.e. let browser use full request
  111. * host name).
  112. * @param {boolean=} opt_secure Whether the cookie should only be sent over
  113. * a secure channel.
  114. */
  115. goog.net.Cookies.prototype.set = function(
  116. name, value, opt_maxAge, opt_path, opt_domain, opt_secure) {
  117. if (!this.isValidName(name)) {
  118. throw Error('Invalid cookie name "' + name + '"');
  119. }
  120. if (!this.isValidValue(value)) {
  121. throw Error('Invalid cookie value "' + value + '"');
  122. }
  123. if (!goog.isDef(opt_maxAge)) {
  124. opt_maxAge = -1;
  125. }
  126. var domainStr = opt_domain ? ';domain=' + opt_domain : '';
  127. var pathStr = opt_path ? ';path=' + opt_path : '';
  128. var secureStr = opt_secure ? ';secure' : '';
  129. var expiresStr;
  130. // Case 1: Set a session cookie.
  131. if (opt_maxAge < 0) {
  132. expiresStr = '';
  133. // Case 2: Remove the cookie.
  134. // Note: We don't tell people about this option in the function doc because
  135. // we prefer people to use remove() to remove cookies.
  136. } else if (opt_maxAge == 0) {
  137. // Note: Don't use Jan 1, 1970 for date because NS 4.76 will try to convert
  138. // it to local time, and if the local time is before Jan 1, 1970, then the
  139. // browser will ignore the Expires attribute altogether.
  140. var pastDate = new Date(1970, 1 /*Feb*/, 1); // Feb 1, 1970
  141. expiresStr = ';expires=' + pastDate.toUTCString();
  142. // Case 3: Set a persistent cookie.
  143. } else {
  144. var futureDate = new Date(goog.now() + opt_maxAge * 1000);
  145. expiresStr = ';expires=' + futureDate.toUTCString();
  146. }
  147. this.setCookie_(
  148. name + '=' + value + domainStr + pathStr + expiresStr + secureStr);
  149. };
  150. /**
  151. * Returns the value for the first cookie with the given name.
  152. * @param {string} name The name of the cookie to get.
  153. * @param {string=} opt_default If not found this is returned instead.
  154. * @return {string|undefined} The value of the cookie. If no cookie is set this
  155. * returns opt_default or undefined if opt_default is not provided.
  156. */
  157. goog.net.Cookies.prototype.get = function(name, opt_default) {
  158. var nameEq = name + '=';
  159. var parts = this.getParts_();
  160. for (var i = 0, part; i < parts.length; i++) {
  161. part = goog.string.trim(parts[i]);
  162. // startsWith
  163. if (part.lastIndexOf(nameEq, 0) == 0) {
  164. return part.substr(nameEq.length);
  165. }
  166. if (part == name) {
  167. return '';
  168. }
  169. }
  170. return opt_default;
  171. };
  172. /**
  173. * Removes and expires a cookie.
  174. * @param {string} name The cookie name.
  175. * @param {string=} opt_path The path of the cookie, or null to expire a cookie
  176. * set at the full request path. If not provided, the default is '/'
  177. * (i.e. path=/).
  178. * @param {string=} opt_domain The domain of the cookie, or null to expire a
  179. * cookie set at the full request host name. If not provided, the default is
  180. * null (i.e. cookie at full request host name).
  181. * @return {boolean} Whether the cookie existed before it was removed.
  182. */
  183. goog.net.Cookies.prototype.remove = function(name, opt_path, opt_domain) {
  184. var rv = this.containsKey(name);
  185. this.set(name, '', 0, opt_path, opt_domain);
  186. return rv;
  187. };
  188. /**
  189. * Gets the names for all the cookies.
  190. * @return {Array<string>} An array with the names of the cookies.
  191. */
  192. goog.net.Cookies.prototype.getKeys = function() {
  193. return this.getKeyValues_().keys;
  194. };
  195. /**
  196. * Gets the values for all the cookies.
  197. * @return {Array<string>} An array with the values of the cookies.
  198. */
  199. goog.net.Cookies.prototype.getValues = function() {
  200. return this.getKeyValues_().values;
  201. };
  202. /**
  203. * @return {boolean} Whether there are any cookies for this document.
  204. */
  205. goog.net.Cookies.prototype.isEmpty = function() {
  206. return !this.getCookie_();
  207. };
  208. /**
  209. * @return {number} The number of cookies for this document.
  210. */
  211. goog.net.Cookies.prototype.getCount = function() {
  212. var cookie = this.getCookie_();
  213. if (!cookie) {
  214. return 0;
  215. }
  216. return this.getParts_().length;
  217. };
  218. /**
  219. * Returns whether there is a cookie with the given name.
  220. * @param {string} key The name of the cookie to test for.
  221. * @return {boolean} Whether there is a cookie by that name.
  222. */
  223. goog.net.Cookies.prototype.containsKey = function(key) {
  224. // substring will return empty string if the key is not found, so the get
  225. // function will only return undefined
  226. return goog.isDef(this.get(key));
  227. };
  228. /**
  229. * Returns whether there is a cookie with the given value. (This is an O(n)
  230. * operation.)
  231. * @param {string} value The value to check for.
  232. * @return {boolean} Whether there is a cookie with that value.
  233. */
  234. goog.net.Cookies.prototype.containsValue = function(value) {
  235. // this O(n) in any case so lets do the trivial thing.
  236. var values = this.getKeyValues_().values;
  237. for (var i = 0; i < values.length; i++) {
  238. if (values[i] == value) {
  239. return true;
  240. }
  241. }
  242. return false;
  243. };
  244. /**
  245. * Removes all cookies for this document. Note that this will only remove
  246. * cookies from the current path and domain. If there are cookies set using a
  247. * subpath and/or another domain these will still be there.
  248. */
  249. goog.net.Cookies.prototype.clear = function() {
  250. var keys = this.getKeyValues_().keys;
  251. for (var i = keys.length - 1; i >= 0; i--) {
  252. this.remove(keys[i]);
  253. }
  254. };
  255. /**
  256. * Private helper function to allow testing cookies without depending on the
  257. * browser.
  258. * @param {string} s The cookie string to set.
  259. * @private
  260. */
  261. goog.net.Cookies.prototype.setCookie_ = function(s) {
  262. this.document_.cookie = s;
  263. };
  264. /**
  265. * Private helper function to allow testing cookies without depending on the
  266. * browser. IE6 can return null here.
  267. * @return {string} Returns the {@code document.cookie}.
  268. * @private
  269. */
  270. goog.net.Cookies.prototype.getCookie_ = function() {
  271. return this.document_.cookie;
  272. };
  273. /**
  274. * @return {!Array<string>} The cookie split on semi colons.
  275. * @private
  276. */
  277. goog.net.Cookies.prototype.getParts_ = function() {
  278. return (this.getCookie_() || '').split(';');
  279. };
  280. /**
  281. * Gets the names and values for all the cookies.
  282. * @return {!{keys:!Array<string>, values:!Array<string>}} An object with keys
  283. * and values.
  284. * @private
  285. */
  286. goog.net.Cookies.prototype.getKeyValues_ = function() {
  287. var parts = this.getParts_();
  288. var keys = [], values = [], index, part;
  289. for (var i = 0; i < parts.length; i++) {
  290. part = goog.string.trim(parts[i]);
  291. index = part.indexOf('=');
  292. if (index == -1) { // empty name
  293. keys.push('');
  294. values.push(part);
  295. } else {
  296. keys.push(part.substring(0, index));
  297. values.push(part.substring(index + 1));
  298. }
  299. }
  300. return {keys: keys, values: values};
  301. };
  302. // TODO(closure-team): This should be a singleton getter instead of a static
  303. // instance.
  304. /**
  305. * A static default instance.
  306. * @const {!goog.net.Cookies}
  307. */
  308. goog.net.cookies =
  309. new goog.net.Cookies(typeof document == 'undefined' ? null : document);
  310. /**
  311. * Getter for the static instance of goog.net.Cookies.
  312. * @return {!goog.net.Cookies}
  313. */
  314. goog.net.Cookies.getInstance = function() {
  315. return goog.net.cookies;
  316. };
  317. /**
  318. * Define the constant on the instance in order not to break many references to
  319. * it.
  320. * @type {number}
  321. * @deprecated Use goog.net.Cookies.MAX_COOKIE_LENGTH instead.
  322. */
  323. goog.net.cookies.MAX_COOKIE_LENGTH = goog.net.Cookies.MAX_COOKIE_LENGTH;