OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Portions of this code based on Mozilla: | |
6 // (netwerk/cookie/src/nsCookieService.cpp) | |
7 /* ***** BEGIN LICENSE BLOCK ***** | |
8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
9 * | |
10 * The contents of this file are subject to the Mozilla Public License Version | |
11 * 1.1 (the "License"); you may not use this file except in compliance with | |
12 * the License. You may obtain a copy of the License at | |
13 * http://www.mozilla.org/MPL/ | |
14 * | |
15 * Software distributed under the License is distributed on an "AS IS" basis, | |
16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
17 * for the specific language governing rights and limitations under the | |
18 * License. | |
19 * | |
20 * The Original Code is mozilla.org code. | |
21 * | |
22 * The Initial Developer of the Original Code is | |
23 * Netscape Communications Corporation. | |
24 * Portions created by the Initial Developer are Copyright (C) 2003 | |
25 * the Initial Developer. All Rights Reserved. | |
26 * | |
27 * Contributor(s): | |
28 * Daniel Witte (dwitte@stanford.edu) | |
29 * Michiel van Leeuwen (mvl@exedo.nl) | |
30 * | |
31 * Alternatively, the contents of this file may be used under the terms of | |
32 * either the GNU General Public License Version 2 or later (the "GPL"), or | |
33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
34 * in which case the provisions of the GPL or the LGPL are applicable instead | |
35 * of those above. If you wish to allow use of your version of this file only | |
36 * under the terms of either the GPL or the LGPL, and not to allow others to | |
37 * use your version of this file under the terms of the MPL, indicate your | |
38 * decision by deleting the provisions above and replace them with the notice | |
39 * and other provisions required by the GPL or the LGPL. If you do not delete | |
40 * the provisions above, a recipient may use your version of this file under | |
41 * the terms of any one of the MPL, the GPL or the LGPL. | |
42 * | |
43 * ***** END LICENSE BLOCK ***** */ | |
44 | |
45 #include "net/base/cookie_monster.h" | |
46 | |
47 #include <algorithm> | |
48 #include <set> | |
49 | |
50 #include "base/basictypes.h" | |
51 #include "base/bind.h" | |
52 #include "base/callback.h" | |
53 #include "base/format_macros.h" | |
54 #include "base/logging.h" | |
55 #include "base/memory/scoped_ptr.h" | |
56 #include "base/message_loop.h" | |
57 #include "base/message_loop_proxy.h" | |
58 #include "base/metrics/histogram.h" | |
59 #include "base/string_tokenizer.h" | |
60 #include "base/string_util.h" | |
61 #include "base/stringprintf.h" | |
62 #include "googleurl/src/gurl.h" | |
63 #include "googleurl/src/url_canon.h" | |
64 #include "net/base/cookie_util.h" | |
65 #include "net/base/registry_controlled_domain.h" | |
66 | |
67 using base::Time; | |
68 using base::TimeDelta; | |
69 using base::TimeTicks; | |
70 | |
71 // In steady state, most cookie requests can be satisfied by the in memory | |
72 // cookie monster store. However, if a request comes in during the initial | |
73 // cookie load, it must be delayed until that load completes. That is done by | |
74 // queueing it on CookieMonster::queue_ and running it when notification of | |
75 // cookie load completion is received via CookieMonster::OnLoaded. This callback | |
76 // is passed to the persistent store from CookieMonster::InitStore(), which is | |
77 // called on the first operation invoked on the CookieMonster. | |
78 // | |
79 // On the browser critical paths (e.g. for loading initial web pages in a | |
80 // session restore) it may take too long to wait for the full load. If a cookie | |
81 // request is for a specific URL, DoCookieTaskForURL is called, which triggers a | |
82 // priority load if the key is not loaded yet by calling PersistentCookieStore | |
83 // :: LoadCookiesForKey. The request is queued in CookieMonster::tasks_queued | |
84 // and executed upon receiving notification of key load completion via | |
85 // CookieMonster::OnKeyLoaded(). If multiple requests for the same eTLD+1 are | |
86 // received before key load completion, only the first request calls | |
87 // PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued | |
88 // in CookieMonster::tasks_queued and executed upon receiving notification of | |
89 // key load completion triggered by the first request for the same eTLD+1. | |
90 | |
91 static const int kMinutesInTenYears = 10 * 365 * 24 * 60; | |
92 | |
93 namespace net { | |
94 | |
95 // See comments at declaration of these variables in cookie_monster.h | |
96 // for details. | |
97 const size_t CookieMonster::kDomainMaxCookies = 180; | |
98 const size_t CookieMonster::kDomainPurgeCookies = 30; | |
99 const size_t CookieMonster::kMaxCookies = 3300; | |
100 const size_t CookieMonster::kPurgeCookies = 300; | |
101 const int CookieMonster::kSafeFromGlobalPurgeDays = 30; | |
102 | |
103 namespace { | |
104 | |
105 typedef std::vector<CookieMonster::CanonicalCookie*> CanonicalCookieVector; | |
106 | |
107 // Default minimum delay after updating a cookie's LastAccessDate before we | |
108 // will update it again. | |
109 const int kDefaultAccessUpdateThresholdSeconds = 60; | |
110 | |
111 // Comparator to sort cookies from highest creation date to lowest | |
112 // creation date. | |
113 struct OrderByCreationTimeDesc { | |
114 bool operator()(const CookieMonster::CookieMap::iterator& a, | |
115 const CookieMonster::CookieMap::iterator& b) const { | |
116 return a->second->CreationDate() > b->second->CreationDate(); | |
117 } | |
118 }; | |
119 | |
120 // Constants for use in VLOG | |
121 const int kVlogPerCookieMonster = 1; | |
122 const int kVlogPeriodic = 3; | |
123 const int kVlogGarbageCollection = 5; | |
124 const int kVlogSetCookies = 7; | |
125 const int kVlogGetCookies = 9; | |
126 | |
127 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
128 const int kPersistentSessionCookieExpiryInDays = 14; | |
129 #endif | |
130 | |
131 // Mozilla sorts on the path length (longest first), and then it | |
132 // sorts by creation time (oldest first). | |
133 // The RFC says the sort order for the domain attribute is undefined. | |
134 bool CookieSorter(CookieMonster::CanonicalCookie* cc1, | |
135 CookieMonster::CanonicalCookie* cc2) { | |
136 if (cc1->Path().length() == cc2->Path().length()) | |
137 return cc1->CreationDate() < cc2->CreationDate(); | |
138 return cc1->Path().length() > cc2->Path().length(); | |
139 } | |
140 | |
141 bool LRUCookieSorter(const CookieMonster::CookieMap::iterator& it1, | |
142 const CookieMonster::CookieMap::iterator& it2) { | |
143 // Cookies accessed less recently should be deleted first. | |
144 if (it1->second->LastAccessDate() != it2->second->LastAccessDate()) | |
145 return it1->second->LastAccessDate() < it2->second->LastAccessDate(); | |
146 | |
147 // In rare cases we might have two cookies with identical last access times. | |
148 // To preserve the stability of the sort, in these cases prefer to delete | |
149 // older cookies over newer ones. CreationDate() is guaranteed to be unique. | |
150 return it1->second->CreationDate() < it2->second->CreationDate(); | |
151 } | |
152 | |
153 // Our strategy to find duplicates is: | |
154 // (1) Build a map from (cookiename, cookiepath) to | |
155 // {list of cookies with this signature, sorted by creation time}. | |
156 // (2) For each list with more than 1 entry, keep the cookie having the | |
157 // most recent creation time, and delete the others. | |
158 // | |
159 // Two cookies are considered equivalent if they have the same domain, | |
160 // name, and path. | |
161 struct CookieSignature { | |
162 public: | |
163 CookieSignature(const std::string& name, const std::string& domain, | |
164 const std::string& path) | |
165 : name(name), | |
166 domain(domain), | |
167 path(path) {} | |
168 | |
169 // To be a key for a map this class needs to be assignable, copyable, | |
170 // and have an operator<. The default assignment operator | |
171 // and copy constructor are exactly what we want. | |
172 | |
173 bool operator<(const CookieSignature& cs) const { | |
174 // Name compare dominates, then domain, then path. | |
175 int diff = name.compare(cs.name); | |
176 if (diff != 0) | |
177 return diff < 0; | |
178 | |
179 diff = domain.compare(cs.domain); | |
180 if (diff != 0) | |
181 return diff < 0; | |
182 | |
183 return path.compare(cs.path) < 0; | |
184 } | |
185 | |
186 std::string name; | |
187 std::string domain; | |
188 std::string path; | |
189 }; | |
190 | |
191 // Determine the cookie domain to use for setting the specified cookie. | |
192 bool GetCookieDomain(const GURL& url, | |
193 const CookieMonster::ParsedCookie& pc, | |
194 std::string* result) { | |
195 std::string domain_string; | |
196 if (pc.HasDomain()) | |
197 domain_string = pc.Domain(); | |
198 return cookie_util::GetCookieDomainWithString(url, domain_string, result); | |
199 } | |
200 | |
201 std::string CanonPathWithString(const GURL& url, | |
202 const std::string& path_string) { | |
203 // The RFC says the path should be a prefix of the current URL path. | |
204 // However, Mozilla allows you to set any path for compatibility with | |
205 // broken websites. We unfortunately will mimic this behavior. We try | |
206 // to be generous and accept cookies with an invalid path attribute, and | |
207 // default the path to something reasonable. | |
208 | |
209 // The path was supplied in the cookie, we'll take it. | |
210 if (!path_string.empty() && path_string[0] == '/') | |
211 return path_string; | |
212 | |
213 // The path was not supplied in the cookie or invalid, we will default | |
214 // to the current URL path. | |
215 // """Defaults to the path of the request URL that generated the | |
216 // Set-Cookie response, up to, but not including, the | |
217 // right-most /.""" | |
218 // How would this work for a cookie on /? We will include it then. | |
219 const std::string& url_path = url.path(); | |
220 | |
221 size_t idx = url_path.find_last_of('/'); | |
222 | |
223 // The cookie path was invalid or a single '/'. | |
224 if (idx == 0 || idx == std::string::npos) | |
225 return std::string("/"); | |
226 | |
227 // Return up to the rightmost '/'. | |
228 return url_path.substr(0, idx); | |
229 } | |
230 | |
231 std::string CanonPath(const GURL& url, | |
232 const CookieMonster::ParsedCookie& pc) { | |
233 std::string path_string; | |
234 if (pc.HasPath()) | |
235 path_string = pc.Path(); | |
236 return CanonPathWithString(url, path_string); | |
237 } | |
238 | |
239 Time CanonExpiration(const CookieMonster::ParsedCookie& pc, | |
240 const Time& current) { | |
241 // First, try the Max-Age attribute. | |
242 uint64 max_age = 0; | |
243 if (pc.HasMaxAge() && | |
244 #ifdef COMPILER_MSVC | |
245 sscanf_s( | |
246 #else | |
247 sscanf( | |
248 #endif | |
249 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { | |
250 return current + TimeDelta::FromSeconds(max_age); | |
251 } | |
252 | |
253 // Try the Expires attribute. | |
254 if (pc.HasExpires()) | |
255 return CookieMonster::ParseCookieTime(pc.Expires()); | |
256 | |
257 // Invalid or no expiration, persistent cookie. | |
258 return Time(); | |
259 } | |
260 | |
261 // Helper for GarbageCollection. If |cookie_its->size() > num_max|, remove the | |
262 // |num_max - num_purge| most recently accessed cookies from cookie_its. | |
263 // (In other words, leave the entries that are candidates for | |
264 // eviction in cookie_its.) The cookies returned will be in order sorted by | |
265 // access time, least recently accessed first. The access time of the least | |
266 // recently accessed entry not returned will be placed in | |
267 // |*lra_removed| if that pointer is set. FindLeastRecentlyAccessed | |
268 // returns false if no manipulation is done (because the list size is less | |
269 // than num_max), true otherwise. | |
270 bool FindLeastRecentlyAccessed( | |
271 size_t num_max, | |
272 size_t num_purge, | |
273 Time* lra_removed, | |
274 std::vector<CookieMonster::CookieMap::iterator>* cookie_its) { | |
275 DCHECK_LE(num_purge, num_max); | |
276 if (cookie_its->size() > num_max) { | |
277 VLOG(kVlogGarbageCollection) | |
278 << "FindLeastRecentlyAccessed() Deep Garbage Collect."; | |
279 num_purge += cookie_its->size() - num_max; | |
280 DCHECK_GT(cookie_its->size(), num_purge); | |
281 | |
282 // Add 1 so that we can get the last time left in the store. | |
283 std::partial_sort(cookie_its->begin(), cookie_its->begin() + num_purge + 1, | |
284 cookie_its->end(), LRUCookieSorter); | |
285 *lra_removed = | |
286 (*(cookie_its->begin() + num_purge))->second->LastAccessDate(); | |
287 cookie_its->erase(cookie_its->begin() + num_purge, cookie_its->end()); | |
288 return true; | |
289 } | |
290 return false; | |
291 } | |
292 | |
293 // Mapping between DeletionCause and Delegate::ChangeCause; the mapping also | |
294 // provides a boolean that specifies whether or not an OnCookieChanged | |
295 // notification ought to be generated. | |
296 typedef struct ChangeCausePair_struct { | |
297 CookieMonster::Delegate::ChangeCause cause; | |
298 bool notify; | |
299 } ChangeCausePair; | |
300 ChangeCausePair ChangeCauseMapping[] = { | |
301 // DELETE_COOKIE_EXPLICIT | |
302 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, true }, | |
303 // DELETE_COOKIE_OVERWRITE | |
304 { CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE, true }, | |
305 // DELETE_COOKIE_EXPIRED | |
306 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED, true }, | |
307 // DELETE_COOKIE_EVICTED | |
308 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
309 // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE | |
310 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }, | |
311 // DELETE_COOKIE_DONT_RECORD | |
312 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }, | |
313 // DELETE_COOKIE_EVICTED_DOMAIN | |
314 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
315 // DELETE_COOKIE_EVICTED_GLOBAL | |
316 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
317 // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE | |
318 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
319 // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE | |
320 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
321 // DELETE_COOKIE_EXPIRED_OVERWRITE | |
322 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true }, | |
323 // DELETE_COOKIE_LAST_ENTRY | |
324 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false } | |
325 }; | |
326 | |
327 std::string BuildCookieLine(const CanonicalCookieVector& cookies) { | |
328 std::string cookie_line; | |
329 for (CanonicalCookieVector::const_iterator it = cookies.begin(); | |
330 it != cookies.end(); ++it) { | |
331 if (it != cookies.begin()) | |
332 cookie_line += "; "; | |
333 // In Mozilla if you set a cookie like AAAA, it will have an empty token | |
334 // and a value of AAAA. When it sends the cookie back, it will send AAAA, | |
335 // so we need to avoid sending =AAAA for a blank token value. | |
336 if (!(*it)->Name().empty()) | |
337 cookie_line += (*it)->Name() + "="; | |
338 cookie_line += (*it)->Value(); | |
339 } | |
340 return cookie_line; | |
341 } | |
342 | |
343 void BuildCookieInfoList(const CanonicalCookieVector& cookies, | |
344 std::vector<CookieStore::CookieInfo>* cookie_infos) { | |
345 for (CanonicalCookieVector::const_iterator it = cookies.begin(); | |
346 it != cookies.end(); ++it) { | |
347 const CookieMonster::CanonicalCookie* cookie = *it; | |
348 CookieStore::CookieInfo cookie_info; | |
349 | |
350 cookie_info.name = cookie->Name(); | |
351 cookie_info.creation_date = cookie->CreationDate(); | |
352 cookie_info.mac_key = cookie->MACKey(); | |
353 cookie_info.mac_algorithm = cookie->MACAlgorithm(); | |
354 | |
355 cookie_infos->push_back(cookie_info); | |
356 } | |
357 } | |
358 | |
359 } // namespace | |
360 | |
361 // static | |
362 bool CookieMonster::enable_file_scheme_ = false; | |
363 | |
364 CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate) | |
365 : initialized_(false), | |
366 loaded_(false), | |
367 store_(store), | |
368 last_access_threshold_( | |
369 TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)), | |
370 delegate_(delegate), | |
371 last_statistic_record_time_(Time::Now()), | |
372 keep_expired_cookies_(false), | |
373 persist_session_cookies_(false) { | |
374 InitializeHistograms(); | |
375 SetDefaultCookieableSchemes(); | |
376 } | |
377 | |
378 CookieMonster::CookieMonster(PersistentCookieStore* store, | |
379 Delegate* delegate, | |
380 int last_access_threshold_milliseconds) | |
381 : initialized_(false), | |
382 loaded_(false), | |
383 store_(store), | |
384 last_access_threshold_(base::TimeDelta::FromMilliseconds( | |
385 last_access_threshold_milliseconds)), | |
386 delegate_(delegate), | |
387 last_statistic_record_time_(base::Time::Now()), | |
388 keep_expired_cookies_(false), | |
389 persist_session_cookies_(false) { | |
390 InitializeHistograms(); | |
391 SetDefaultCookieableSchemes(); | |
392 } | |
393 | |
394 // Parse a cookie expiration time. We try to be lenient, but we need to | |
395 // assume some order to distinguish the fields. The basic rules: | |
396 // - The month name must be present and prefix the first 3 letters of the | |
397 // full month name (jan for January, jun for June). | |
398 // - If the year is <= 2 digits, it must occur after the day of month. | |
399 // - The time must be of the format hh:mm:ss. | |
400 // An average cookie expiration will look something like this: | |
401 // Sat, 15-Apr-17 21:01:22 GMT | |
402 Time CookieMonster::ParseCookieTime(const std::string& time_string) { | |
403 static const char* kMonths[] = { "jan", "feb", "mar", "apr", "may", "jun", | |
404 "jul", "aug", "sep", "oct", "nov", "dec" }; | |
405 static const int kMonthsLen = arraysize(kMonths); | |
406 // We want to be pretty liberal, and support most non-ascii and non-digit | |
407 // characters as a delimiter. We can't treat : as a delimiter, because it | |
408 // is the delimiter for hh:mm:ss, and we want to keep this field together. | |
409 // We make sure to include - and +, since they could prefix numbers. | |
410 // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes | |
411 // will be preserved, and we will get them here. So we make sure to include | |
412 // quote characters, and also \ for anything that was internally escaped. | |
413 static const char* kDelimiters = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~"; | |
414 | |
415 Time::Exploded exploded = {0}; | |
416 | |
417 StringTokenizer tokenizer(time_string, kDelimiters); | |
418 | |
419 bool found_day_of_month = false; | |
420 bool found_month = false; | |
421 bool found_time = false; | |
422 bool found_year = false; | |
423 | |
424 while (tokenizer.GetNext()) { | |
425 const std::string token = tokenizer.token(); | |
426 DCHECK(!token.empty()); | |
427 bool numerical = IsAsciiDigit(token[0]); | |
428 | |
429 // String field | |
430 if (!numerical) { | |
431 if (!found_month) { | |
432 for (int i = 0; i < kMonthsLen; ++i) { | |
433 // Match prefix, so we could match January, etc | |
434 if (base::strncasecmp(token.c_str(), kMonths[i], 3) == 0) { | |
435 exploded.month = i + 1; | |
436 found_month = true; | |
437 break; | |
438 } | |
439 } | |
440 } else { | |
441 // If we've gotten here, it means we've already found and parsed our | |
442 // month, and we have another string, which we would expect to be the | |
443 // the time zone name. According to the RFC and my experiments with | |
444 // how sites format their expirations, we don't have much of a reason | |
445 // to support timezones. We don't want to ever barf on user input, | |
446 // but this DCHECK should pass for well-formed data. | |
447 // DCHECK(token == "GMT"); | |
448 } | |
449 // Numeric field w/ a colon | |
450 } else if (token.find(':') != std::string::npos) { | |
451 if (!found_time && | |
452 #ifdef COMPILER_MSVC | |
453 sscanf_s( | |
454 #else | |
455 sscanf( | |
456 #endif | |
457 token.c_str(), "%2u:%2u:%2u", &exploded.hour, | |
458 &exploded.minute, &exploded.second) == 3) { | |
459 found_time = true; | |
460 } else { | |
461 // We should only ever encounter one time-like thing. If we're here, | |
462 // it means we've found a second, which shouldn't happen. We keep | |
463 // the first. This check should be ok for well-formed input: | |
464 // NOTREACHED(); | |
465 } | |
466 // Numeric field | |
467 } else { | |
468 // Overflow with atoi() is unspecified, so we enforce a max length. | |
469 if (!found_day_of_month && token.length() <= 2) { | |
470 exploded.day_of_month = atoi(token.c_str()); | |
471 found_day_of_month = true; | |
472 } else if (!found_year && token.length() <= 5) { | |
473 exploded.year = atoi(token.c_str()); | |
474 found_year = true; | |
475 } else { | |
476 // If we're here, it means we've either found an extra numeric field, | |
477 // or a numeric field which was too long. For well-formed input, the | |
478 // following check would be reasonable: | |
479 // NOTREACHED(); | |
480 } | |
481 } | |
482 } | |
483 | |
484 if (!found_day_of_month || !found_month || !found_time || !found_year) { | |
485 // We didn't find all of the fields we need. For well-formed input, the | |
486 // following check would be reasonable: | |
487 // NOTREACHED() << "Cookie parse expiration failed: " << time_string; | |
488 return Time(); | |
489 } | |
490 | |
491 // Normalize the year to expand abbreviated years to the full year. | |
492 if (exploded.year >= 69 && exploded.year <= 99) | |
493 exploded.year += 1900; | |
494 if (exploded.year >= 0 && exploded.year <= 68) | |
495 exploded.year += 2000; | |
496 | |
497 // If our values are within their correct ranges, we got our time. | |
498 if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 && | |
499 exploded.month >= 1 && exploded.month <= 12 && | |
500 exploded.year >= 1601 && exploded.year <= 30827 && | |
501 exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) { | |
502 return Time::FromUTCExploded(exploded); | |
503 } | |
504 | |
505 // One of our values was out of expected range. For well-formed input, | |
506 // the following check would be reasonable: | |
507 // NOTREACHED() << "Cookie exploded expiration failed: " << time_string; | |
508 | |
509 return Time(); | |
510 } | |
511 | |
512 // Task classes for queueing the coming request. | |
513 | |
514 class CookieMonster::CookieMonsterTask | |
515 : public base::RefCountedThreadSafe<CookieMonsterTask> { | |
516 public: | |
517 // Runs the task and invokes the client callback on the thread that | |
518 // originally constructed the task. | |
519 virtual void Run() = 0; | |
520 | |
521 protected: | |
522 explicit CookieMonsterTask(CookieMonster* cookie_monster); | |
523 virtual ~CookieMonsterTask(); | |
524 | |
525 // Invokes the callback immediately, if the current thread is the one | |
526 // that originated the task, or queues the callback for execution on the | |
527 // appropriate thread. Maintains a reference to this CookieMonsterTask | |
528 // instance until the callback completes. | |
529 void InvokeCallback(base::Closure callback); | |
530 | |
531 CookieMonster* cookie_monster() { | |
532 return cookie_monster_; | |
533 } | |
534 | |
535 friend class base::RefCountedThreadSafe<CookieMonsterTask>; | |
536 | |
537 private: | |
538 CookieMonster* cookie_monster_; | |
539 scoped_refptr<base::MessageLoopProxy> thread_; | |
540 | |
541 DISALLOW_COPY_AND_ASSIGN(CookieMonsterTask); | |
542 }; | |
543 | |
544 CookieMonster::CookieMonsterTask::CookieMonsterTask( | |
545 CookieMonster* cookie_monster) | |
546 : cookie_monster_(cookie_monster), | |
547 thread_(base::MessageLoopProxy::current()) { } | |
548 | |
549 CookieMonster::CookieMonsterTask::~CookieMonsterTask() { } | |
550 | |
551 // Unfortunately, one cannot re-bind a Callback with parameters into a closure. | |
552 // Therefore, the closure passed to InvokeCallback is a clumsy binding of | |
553 // Callback::Run on a wrapped Callback instance. Since Callback is not | |
554 // reference counted, we bind to an instance that is a member of the | |
555 // CookieMonsterTask subclass. Then, we cannot simply post the callback to a | |
556 // message loop because the underlying instance may be destroyed (along with the | |
557 // CookieMonsterTask instance) in the interim. Therefore, we post a callback | |
558 // bound to the CookieMonsterTask, which *is* reference counted (thus preventing | |
559 // destruction of the original callback), and which invokes the closure (which | |
560 // invokes the original callback with the returned data). | |
561 void CookieMonster::CookieMonsterTask::InvokeCallback(base::Closure callback) { | |
562 if (thread_->BelongsToCurrentThread()) { | |
563 callback.Run(); | |
564 } else { | |
565 thread_->PostTask(FROM_HERE, base::Bind( | |
566 &CookieMonster::CookieMonsterTask::InvokeCallback, this, callback)); | |
567 } | |
568 } | |
569 | |
570 // Task class for SetCookieWithDetails call. | |
571 class CookieMonster::SetCookieWithDetailsTask | |
572 : public CookieMonster::CookieMonsterTask { | |
573 public: | |
574 SetCookieWithDetailsTask( | |
575 CookieMonster* cookie_monster, | |
576 const GURL& url, const std::string& name, const std::string& value, | |
577 const std::string& domain, const std::string& path, | |
578 const base::Time& expiration_time, bool secure, bool http_only, | |
579 const CookieMonster::SetCookiesCallback& callback) | |
580 : CookieMonsterTask(cookie_monster), | |
581 url_(url), | |
582 name_(name), | |
583 value_(value), | |
584 domain_(domain), | |
585 path_(path), | |
586 expiration_time_(expiration_time), | |
587 secure_(secure), | |
588 http_only_(http_only), | |
589 callback_(callback) { } | |
590 | |
591 virtual void Run() OVERRIDE; | |
592 | |
593 private: | |
594 GURL url_; | |
595 std::string name_; | |
596 std::string value_; | |
597 std::string domain_; | |
598 std::string path_; | |
599 base::Time expiration_time_; | |
600 bool secure_; | |
601 bool http_only_; | |
602 CookieMonster::SetCookiesCallback callback_; | |
603 | |
604 DISALLOW_COPY_AND_ASSIGN(SetCookieWithDetailsTask); | |
605 }; | |
606 | |
607 void CookieMonster::SetCookieWithDetailsTask::Run() { | |
608 bool success = this->cookie_monster()-> | |
609 SetCookieWithDetails(url_, name_, value_, domain_, path_, | |
610 expiration_time_, secure_, http_only_); | |
611 if (!callback_.is_null()) { | |
612 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run, | |
613 base::Unretained(&callback_), success)); | |
614 } | |
615 } | |
616 | |
617 // Task class for GetAllCookies call. | |
618 class CookieMonster::GetAllCookiesTask | |
619 : public CookieMonster::CookieMonsterTask { | |
620 public: | |
621 GetAllCookiesTask(CookieMonster* cookie_monster, | |
622 const CookieMonster::GetCookieListCallback& callback) | |
623 : CookieMonsterTask(cookie_monster), | |
624 callback_(callback) { } | |
625 | |
626 virtual void Run() OVERRIDE; | |
627 | |
628 private: | |
629 CookieMonster::GetCookieListCallback callback_; | |
630 | |
631 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesTask); | |
632 }; | |
633 | |
634 void CookieMonster::GetAllCookiesTask::Run() { | |
635 if (!callback_.is_null()) { | |
636 CookieList cookies = this->cookie_monster()->GetAllCookies(); | |
637 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run, | |
638 base::Unretained(&callback_), cookies)); | |
639 } | |
640 } | |
641 | |
642 // Task class for GetAllCookiesForURLWithOptions call. | |
643 class CookieMonster::GetAllCookiesForURLWithOptionsTask | |
644 : public CookieMonster::CookieMonsterTask { | |
645 public: | |
646 GetAllCookiesForURLWithOptionsTask( | |
647 CookieMonster* cookie_monster, | |
648 const GURL& url, | |
649 const CookieOptions& options, | |
650 const CookieMonster::GetCookieListCallback& callback) | |
651 : CookieMonsterTask(cookie_monster), | |
652 url_(url), | |
653 options_(options), | |
654 callback_(callback) { } | |
655 | |
656 virtual void Run() OVERRIDE; | |
657 | |
658 private: | |
659 GURL url_; | |
660 CookieOptions options_; | |
661 CookieMonster::GetCookieListCallback callback_; | |
662 | |
663 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesForURLWithOptionsTask); | |
664 }; | |
665 | |
666 void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() { | |
667 if (!callback_.is_null()) { | |
668 CookieList cookies = this->cookie_monster()-> | |
669 GetAllCookiesForURLWithOptions(url_, options_); | |
670 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run, | |
671 base::Unretained(&callback_), cookies)); | |
672 } | |
673 } | |
674 | |
675 // Task class for DeleteAll call. | |
676 class CookieMonster::DeleteAllTask : public CookieMonster::CookieMonsterTask { | |
677 public: | |
678 DeleteAllTask(CookieMonster* cookie_monster, | |
679 const CookieMonster::DeleteCallback& callback) | |
680 : CookieMonsterTask(cookie_monster), | |
681 callback_(callback) { } | |
682 | |
683 virtual void Run() OVERRIDE; | |
684 | |
685 private: | |
686 CookieMonster::DeleteCallback callback_; | |
687 | |
688 DISALLOW_COPY_AND_ASSIGN(DeleteAllTask); | |
689 }; | |
690 | |
691 void CookieMonster::DeleteAllTask::Run() { | |
692 int num_deleted = this->cookie_monster()->DeleteAll(true); | |
693 if (!callback_.is_null()) { | |
694 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
695 base::Unretained(&callback_), num_deleted)); | |
696 } | |
697 } | |
698 | |
699 // Task class for DeleteAllCreatedBetween call. | |
700 class CookieMonster::DeleteAllCreatedBetweenTask | |
701 : public CookieMonster::CookieMonsterTask { | |
702 public: | |
703 DeleteAllCreatedBetweenTask( | |
704 CookieMonster* cookie_monster, | |
705 const Time& delete_begin, | |
706 const Time& delete_end, | |
707 const CookieMonster::DeleteCallback& callback) | |
708 : CookieMonsterTask(cookie_monster), | |
709 delete_begin_(delete_begin), | |
710 delete_end_(delete_end), | |
711 callback_(callback) { } | |
712 | |
713 virtual void Run() OVERRIDE; | |
714 | |
715 private: | |
716 Time delete_begin_; | |
717 Time delete_end_; | |
718 CookieMonster::DeleteCallback callback_; | |
719 | |
720 DISALLOW_COPY_AND_ASSIGN(DeleteAllCreatedBetweenTask); | |
721 }; | |
722 | |
723 void CookieMonster::DeleteAllCreatedBetweenTask::Run() { | |
724 int num_deleted = this->cookie_monster()-> | |
725 DeleteAllCreatedBetween(delete_begin_, delete_end_); | |
726 if (!callback_.is_null()) { | |
727 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
728 base::Unretained(&callback_), num_deleted)); | |
729 } | |
730 } | |
731 | |
732 // Task class for DeleteAllForHost call. | |
733 class CookieMonster::DeleteAllForHostTask | |
734 : public CookieMonster::CookieMonsterTask { | |
735 public: | |
736 DeleteAllForHostTask(CookieMonster* cookie_monster, | |
737 const GURL& url, | |
738 const CookieMonster::DeleteCallback& callback) | |
739 : CookieMonsterTask(cookie_monster), | |
740 url_(url), | |
741 callback_(callback) { } | |
742 | |
743 virtual void Run() OVERRIDE; | |
744 | |
745 private: | |
746 GURL url_; | |
747 CookieMonster::DeleteCallback callback_; | |
748 | |
749 DISALLOW_COPY_AND_ASSIGN(DeleteAllForHostTask); | |
750 }; | |
751 | |
752 void CookieMonster::DeleteAllForHostTask::Run() { | |
753 int num_deleted = this->cookie_monster()->DeleteAllForHost(url_); | |
754 if (!callback_.is_null()) { | |
755 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
756 base::Unretained(&callback_), num_deleted)); | |
757 } | |
758 } | |
759 | |
760 // Task class for DeleteCanonicalCookie call. | |
761 class CookieMonster::DeleteCanonicalCookieTask | |
762 : public CookieMonster::CookieMonsterTask { | |
763 public: | |
764 DeleteCanonicalCookieTask( | |
765 CookieMonster* cookie_monster, | |
766 const CookieMonster::CanonicalCookie& cookie, | |
767 const CookieMonster::DeleteCookieCallback& callback) | |
768 : CookieMonsterTask(cookie_monster), | |
769 cookie_(cookie), | |
770 callback_(callback) { } | |
771 | |
772 virtual void Run() OVERRIDE; | |
773 | |
774 private: | |
775 CookieMonster::CanonicalCookie cookie_; | |
776 CookieMonster::DeleteCookieCallback callback_; | |
777 | |
778 DISALLOW_COPY_AND_ASSIGN(DeleteCanonicalCookieTask); | |
779 }; | |
780 | |
781 void CookieMonster::DeleteCanonicalCookieTask::Run() { | |
782 bool result = this->cookie_monster()->DeleteCanonicalCookie(cookie_); | |
783 if (!callback_.is_null()) { | |
784 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCookieCallback::Run, | |
785 base::Unretained(&callback_), result)); | |
786 } | |
787 } | |
788 | |
789 // Task class for SetCookieWithOptions call. | |
790 class CookieMonster::SetCookieWithOptionsTask | |
791 : public CookieMonster::CookieMonsterTask { | |
792 public: | |
793 SetCookieWithOptionsTask(CookieMonster* cookie_monster, | |
794 const GURL& url, | |
795 const std::string& cookie_line, | |
796 const CookieOptions& options, | |
797 const CookieMonster::SetCookiesCallback& callback) | |
798 : CookieMonsterTask(cookie_monster), | |
799 url_(url), | |
800 cookie_line_(cookie_line), | |
801 options_(options), | |
802 callback_(callback) { } | |
803 | |
804 virtual void Run() OVERRIDE; | |
805 | |
806 private: | |
807 GURL url_; | |
808 std::string cookie_line_; | |
809 CookieOptions options_; | |
810 CookieMonster::SetCookiesCallback callback_; | |
811 | |
812 DISALLOW_COPY_AND_ASSIGN(SetCookieWithOptionsTask); | |
813 }; | |
814 | |
815 void CookieMonster::SetCookieWithOptionsTask::Run() { | |
816 bool result = this->cookie_monster()-> | |
817 SetCookieWithOptions(url_, cookie_line_, options_); | |
818 if (!callback_.is_null()) { | |
819 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run, | |
820 base::Unretained(&callback_), result)); | |
821 } | |
822 } | |
823 | |
824 // Task class for GetCookiesWithOptions call. | |
825 class CookieMonster::GetCookiesWithOptionsTask | |
826 : public CookieMonster::CookieMonsterTask { | |
827 public: | |
828 GetCookiesWithOptionsTask(CookieMonster* cookie_monster, | |
829 const GURL& url, | |
830 const CookieOptions& options, | |
831 const CookieMonster::GetCookiesCallback& callback) | |
832 : CookieMonsterTask(cookie_monster), | |
833 url_(url), | |
834 options_(options), | |
835 callback_(callback) { } | |
836 | |
837 virtual void Run() OVERRIDE; | |
838 | |
839 private: | |
840 GURL url_; | |
841 CookieOptions options_; | |
842 CookieMonster::GetCookiesCallback callback_; | |
843 | |
844 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithOptionsTask); | |
845 }; | |
846 | |
847 void CookieMonster::GetCookiesWithOptionsTask::Run() { | |
848 std::string cookie = this->cookie_monster()-> | |
849 GetCookiesWithOptions(url_, options_); | |
850 if (!callback_.is_null()) { | |
851 this->InvokeCallback(base::Bind(&CookieMonster::GetCookiesCallback::Run, | |
852 base::Unretained(&callback_), cookie)); | |
853 } | |
854 } | |
855 | |
856 // Task class for GetCookiesWithInfo call. | |
857 class CookieMonster::GetCookiesWithInfoTask | |
858 : public CookieMonster::CookieMonsterTask { | |
859 public: | |
860 GetCookiesWithInfoTask(CookieMonster* cookie_monster, | |
861 const GURL& url, | |
862 const CookieOptions& options, | |
863 const CookieMonster::GetCookieInfoCallback& callback) | |
864 : CookieMonsterTask(cookie_monster), | |
865 url_(url), | |
866 options_(options), | |
867 callback_(callback) { } | |
868 | |
869 virtual void Run() OVERRIDE; | |
870 | |
871 private: | |
872 GURL url_; | |
873 CookieOptions options_; | |
874 CookieMonster::GetCookieInfoCallback callback_; | |
875 | |
876 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithInfoTask); | |
877 }; | |
878 | |
879 void CookieMonster::GetCookiesWithInfoTask::Run() { | |
880 if (!callback_.is_null()) { | |
881 std::string cookie_line; | |
882 std::vector<CookieMonster::CookieInfo> cookie_infos; | |
883 this->cookie_monster()-> | |
884 GetCookiesWithInfo(url_, options_, &cookie_line, &cookie_infos); | |
885 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieInfoCallback::Run, | |
886 base::Unretained(&callback_), | |
887 cookie_line, cookie_infos)); | |
888 } | |
889 } | |
890 | |
891 // Task class for DeleteCookie call. | |
892 class CookieMonster::DeleteCookieTask | |
893 : public CookieMonster::CookieMonsterTask { | |
894 public: | |
895 DeleteCookieTask(CookieMonster* cookie_monster, | |
896 const GURL& url, | |
897 const std::string& cookie_name, | |
898 const base::Closure& callback) | |
899 : CookieMonsterTask(cookie_monster), | |
900 url_(url), | |
901 cookie_name_(cookie_name), | |
902 callback_(callback) { } | |
903 | |
904 virtual void Run() OVERRIDE; | |
905 | |
906 private: | |
907 GURL url_; | |
908 std::string cookie_name_; | |
909 base::Closure callback_; | |
910 | |
911 DISALLOW_COPY_AND_ASSIGN(DeleteCookieTask); | |
912 }; | |
913 | |
914 void CookieMonster::DeleteCookieTask::Run() { | |
915 this->cookie_monster()->DeleteCookie(url_, cookie_name_); | |
916 if (!callback_.is_null()) { | |
917 this->InvokeCallback(callback_); | |
918 } | |
919 } | |
920 | |
921 // Asynchronous CookieMonster API | |
922 | |
923 void CookieMonster::SetCookieWithDetailsAsync( | |
924 const GURL& url, const std::string& name, const std::string& value, | |
925 const std::string& domain, const std::string& path, | |
926 const base::Time& expiration_time, bool secure, bool http_only, | |
927 const SetCookiesCallback& callback) { | |
928 scoped_refptr<SetCookieWithDetailsTask> task = | |
929 new SetCookieWithDetailsTask(this, url, name, value, domain, path, | |
930 expiration_time, secure, http_only, | |
931 callback); | |
932 | |
933 DoCookieTaskForURL(task, url); | |
934 } | |
935 | |
936 void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) { | |
937 scoped_refptr<GetAllCookiesTask> task = | |
938 new GetAllCookiesTask(this, callback); | |
939 | |
940 DoCookieTask(task); | |
941 } | |
942 | |
943 | |
944 void CookieMonster::GetAllCookiesForURLWithOptionsAsync( | |
945 const GURL& url, | |
946 const CookieOptions& options, | |
947 const GetCookieListCallback& callback) { | |
948 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = | |
949 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); | |
950 | |
951 DoCookieTaskForURL(task, url); | |
952 } | |
953 | |
954 void CookieMonster::GetAllCookiesForURLAsync( | |
955 const GURL& url, const GetCookieListCallback& callback) { | |
956 CookieOptions options; | |
957 options.set_include_httponly(); | |
958 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = | |
959 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); | |
960 | |
961 DoCookieTaskForURL(task, url); | |
962 } | |
963 | |
964 void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) { | |
965 scoped_refptr<DeleteAllTask> task = | |
966 new DeleteAllTask(this, callback); | |
967 | |
968 DoCookieTask(task); | |
969 } | |
970 | |
971 void CookieMonster::DeleteAllCreatedBetweenAsync( | |
972 const Time& delete_begin, const Time& delete_end, | |
973 const DeleteCallback& callback) { | |
974 scoped_refptr<DeleteAllCreatedBetweenTask> task = | |
975 new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end, | |
976 callback); | |
977 | |
978 DoCookieTask(task); | |
979 } | |
980 | |
981 void CookieMonster::DeleteAllForHostAsync( | |
982 const GURL& url, const DeleteCallback& callback) { | |
983 scoped_refptr<DeleteAllForHostTask> task = | |
984 new DeleteAllForHostTask(this, url, callback); | |
985 | |
986 DoCookieTaskForURL(task, url); | |
987 } | |
988 | |
989 void CookieMonster::DeleteCanonicalCookieAsync( | |
990 const CanonicalCookie& cookie, | |
991 const DeleteCookieCallback& callback) { | |
992 scoped_refptr<DeleteCanonicalCookieTask> task = | |
993 new DeleteCanonicalCookieTask(this, cookie, callback); | |
994 | |
995 DoCookieTask(task); | |
996 } | |
997 | |
998 void CookieMonster::SetCookieWithOptionsAsync( | |
999 const GURL& url, | |
1000 const std::string& cookie_line, | |
1001 const CookieOptions& options, | |
1002 const SetCookiesCallback& callback) { | |
1003 scoped_refptr<SetCookieWithOptionsTask> task = | |
1004 new SetCookieWithOptionsTask(this, url, cookie_line, options, callback); | |
1005 | |
1006 DoCookieTaskForURL(task, url); | |
1007 } | |
1008 | |
1009 void CookieMonster::GetCookiesWithOptionsAsync( | |
1010 const GURL& url, | |
1011 const CookieOptions& options, | |
1012 const GetCookiesCallback& callback) { | |
1013 scoped_refptr<GetCookiesWithOptionsTask> task = | |
1014 new GetCookiesWithOptionsTask(this, url, options, callback); | |
1015 | |
1016 DoCookieTaskForURL(task, url); | |
1017 } | |
1018 | |
1019 void CookieMonster::GetCookiesWithInfoAsync( | |
1020 const GURL& url, | |
1021 const CookieOptions& options, | |
1022 const GetCookieInfoCallback& callback) { | |
1023 scoped_refptr<GetCookiesWithInfoTask> task = | |
1024 new GetCookiesWithInfoTask(this, url, options, callback); | |
1025 | |
1026 DoCookieTaskForURL(task, url); | |
1027 } | |
1028 | |
1029 void CookieMonster::DeleteCookieAsync(const GURL& url, | |
1030 const std::string& cookie_name, | |
1031 const base::Closure& callback) { | |
1032 scoped_refptr<DeleteCookieTask> task = | |
1033 new DeleteCookieTask(this, url, cookie_name, callback); | |
1034 | |
1035 DoCookieTaskForURL(task, url); | |
1036 } | |
1037 | |
1038 void CookieMonster::DoCookieTask( | |
1039 const scoped_refptr<CookieMonsterTask>& task_item) { | |
1040 { | |
1041 base::AutoLock autolock(lock_); | |
1042 InitIfNecessary(); | |
1043 if (!loaded_) { | |
1044 queue_.push(task_item); | |
1045 return; | |
1046 } | |
1047 } | |
1048 | |
1049 task_item->Run(); | |
1050 } | |
1051 | |
1052 void CookieMonster::DoCookieTaskForURL( | |
1053 const scoped_refptr<CookieMonsterTask>& task_item, | |
1054 const GURL& url) { | |
1055 { | |
1056 base::AutoLock autolock(lock_); | |
1057 InitIfNecessary(); | |
1058 // If cookies for the requested domain key (eTLD+1) have been loaded from DB | |
1059 // then run the task, otherwise load from DB. | |
1060 if (!loaded_) { | |
1061 // Checks if the domain key has been loaded. | |
1062 std::string key(cookie_util::GetEffectiveDomain(url.scheme(), | |
1063 url.host())); | |
1064 if (keys_loaded_.find(key) == keys_loaded_.end()) { | |
1065 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > | |
1066 ::iterator it = tasks_queued_.find(key); | |
1067 if (it == tasks_queued_.end()) { | |
1068 store_->LoadCookiesForKey(key, | |
1069 base::Bind(&CookieMonster::OnKeyLoaded, this, key)); | |
1070 it = tasks_queued_.insert(std::make_pair(key, | |
1071 std::deque<scoped_refptr<CookieMonsterTask> >())).first; | |
1072 } | |
1073 it->second.push_back(task_item); | |
1074 return; | |
1075 } | |
1076 } | |
1077 } | |
1078 task_item->Run(); | |
1079 } | |
1080 | |
1081 bool CookieMonster::SetCookieWithDetails( | |
1082 const GURL& url, const std::string& name, const std::string& value, | |
1083 const std::string& domain, const std::string& path, | |
1084 const base::Time& expiration_time, bool secure, bool http_only) { | |
1085 base::AutoLock autolock(lock_); | |
1086 | |
1087 if (!HasCookieableScheme(url)) | |
1088 return false; | |
1089 | |
1090 Time creation_time = CurrentTime(); | |
1091 last_time_seen_ = creation_time; | |
1092 | |
1093 // TODO(abarth): Take these values as parameters. | |
1094 std::string mac_key; | |
1095 std::string mac_algorithm; | |
1096 | |
1097 scoped_ptr<CanonicalCookie> cc; | |
1098 cc.reset(CanonicalCookie::Create( | |
1099 url, name, value, domain, path, | |
1100 mac_key, mac_algorithm, | |
1101 creation_time, expiration_time, | |
1102 secure, http_only, !expiration_time.is_null())); | |
1103 | |
1104 if (!cc.get()) | |
1105 return false; | |
1106 | |
1107 CookieOptions options; | |
1108 options.set_include_httponly(); | |
1109 return SetCanonicalCookie(&cc, creation_time, options); | |
1110 } | |
1111 | |
1112 bool CookieMonster::InitializeFrom(const CookieList& list) { | |
1113 base::AutoLock autolock(lock_); | |
1114 InitIfNecessary(); | |
1115 for (net::CookieList::const_iterator iter = list.begin(); | |
1116 iter != list.end(); ++iter) { | |
1117 scoped_ptr<net::CookieMonster::CanonicalCookie> cookie; | |
1118 cookie.reset(new net::CookieMonster::CanonicalCookie(*iter)); | |
1119 net::CookieOptions options; | |
1120 options.set_include_httponly(); | |
1121 if (!SetCanonicalCookie(&cookie, cookie->CreationDate(), | |
1122 options)) { | |
1123 return false; | |
1124 } | |
1125 } | |
1126 return true; | |
1127 } | |
1128 | |
1129 CookieList CookieMonster::GetAllCookies() { | |
1130 base::AutoLock autolock(lock_); | |
1131 | |
1132 // This function is being called to scrape the cookie list for management UI | |
1133 // or similar. We shouldn't show expired cookies in this list since it will | |
1134 // just be confusing to users, and this function is called rarely enough (and | |
1135 // is already slow enough) that it's OK to take the time to garbage collect | |
1136 // the expired cookies now. | |
1137 // | |
1138 // Note that this does not prune cookies to be below our limits (if we've | |
1139 // exceeded them) the way that calling GarbageCollect() would. | |
1140 GarbageCollectExpired(Time::Now(), | |
1141 CookieMapItPair(cookies_.begin(), cookies_.end()), | |
1142 NULL); | |
1143 | |
1144 // Copy the CanonicalCookie pointers from the map so that we can use the same | |
1145 // sorter as elsewhere, then copy the result out. | |
1146 std::vector<CanonicalCookie*> cookie_ptrs; | |
1147 cookie_ptrs.reserve(cookies_.size()); | |
1148 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end(); ++it) | |
1149 cookie_ptrs.push_back(it->second); | |
1150 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter); | |
1151 | |
1152 CookieList cookie_list; | |
1153 cookie_list.reserve(cookie_ptrs.size()); | |
1154 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin(); | |
1155 it != cookie_ptrs.end(); ++it) | |
1156 cookie_list.push_back(**it); | |
1157 | |
1158 return cookie_list; | |
1159 } | |
1160 | |
1161 CookieList CookieMonster::GetAllCookiesForURLWithOptions( | |
1162 const GURL& url, | |
1163 const CookieOptions& options) { | |
1164 base::AutoLock autolock(lock_); | |
1165 | |
1166 std::vector<CanonicalCookie*> cookie_ptrs; | |
1167 FindCookiesForHostAndDomain(url, options, false, &cookie_ptrs); | |
1168 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter); | |
1169 | |
1170 CookieList cookies; | |
1171 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin(); | |
1172 it != cookie_ptrs.end(); it++) | |
1173 cookies.push_back(**it); | |
1174 | |
1175 return cookies; | |
1176 } | |
1177 | |
1178 CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) { | |
1179 CookieOptions options; | |
1180 options.set_include_httponly(); | |
1181 | |
1182 return GetAllCookiesForURLWithOptions(url, options); | |
1183 } | |
1184 | |
1185 int CookieMonster::DeleteAll(bool sync_to_store) { | |
1186 base::AutoLock autolock(lock_); | |
1187 | |
1188 int num_deleted = 0; | |
1189 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
1190 CookieMap::iterator curit = it; | |
1191 ++it; | |
1192 InternalDeleteCookie(curit, sync_to_store, | |
1193 sync_to_store ? DELETE_COOKIE_EXPLICIT : | |
1194 DELETE_COOKIE_DONT_RECORD /* Destruction. */); | |
1195 ++num_deleted; | |
1196 } | |
1197 | |
1198 return num_deleted; | |
1199 } | |
1200 | |
1201 int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin, | |
1202 const Time& delete_end) { | |
1203 base::AutoLock autolock(lock_); | |
1204 | |
1205 int num_deleted = 0; | |
1206 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
1207 CookieMap::iterator curit = it; | |
1208 CanonicalCookie* cc = curit->second; | |
1209 ++it; | |
1210 | |
1211 if (cc->CreationDate() >= delete_begin && | |
1212 (delete_end.is_null() || cc->CreationDate() < delete_end)) { | |
1213 InternalDeleteCookie(curit, | |
1214 true, /*sync_to_store*/ | |
1215 DELETE_COOKIE_EXPLICIT); | |
1216 ++num_deleted; | |
1217 } | |
1218 } | |
1219 | |
1220 return num_deleted; | |
1221 } | |
1222 | |
1223 int CookieMonster::DeleteAllForHost(const GURL& url) { | |
1224 base::AutoLock autolock(lock_); | |
1225 | |
1226 if (!HasCookieableScheme(url)) | |
1227 return 0; | |
1228 | |
1229 const std::string scheme(url.scheme()); | |
1230 const std::string host(url.host()); | |
1231 | |
1232 // We store host cookies in the store by their canonical host name; | |
1233 // domain cookies are stored with a leading ".". So this is a pretty | |
1234 // simple lookup and per-cookie delete. | |
1235 int num_deleted = 0; | |
1236 for (CookieMapItPair its = cookies_.equal_range(GetKey(host)); | |
1237 its.first != its.second;) { | |
1238 CookieMap::iterator curit = its.first; | |
1239 ++its.first; | |
1240 | |
1241 const CanonicalCookie* const cc = curit->second; | |
1242 | |
1243 // Delete only on a match as a host cookie. | |
1244 if (cc->IsHostCookie() && cc->IsDomainMatch(scheme, host)) { | |
1245 num_deleted++; | |
1246 | |
1247 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT); | |
1248 } | |
1249 } | |
1250 return num_deleted; | |
1251 } | |
1252 | |
1253 bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) { | |
1254 base::AutoLock autolock(lock_); | |
1255 | |
1256 for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain())); | |
1257 its.first != its.second; ++its.first) { | |
1258 // The creation date acts as our unique index... | |
1259 if (its.first->second->CreationDate() == cookie.CreationDate()) { | |
1260 InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT); | |
1261 return true; | |
1262 } | |
1263 } | |
1264 return false; | |
1265 } | |
1266 | |
1267 void CookieMonster::SetCookieableSchemes( | |
1268 const char* schemes[], size_t num_schemes) { | |
1269 base::AutoLock autolock(lock_); | |
1270 | |
1271 // Cookieable Schemes must be set before first use of function. | |
1272 DCHECK(!initialized_); | |
1273 | |
1274 cookieable_schemes_.clear(); | |
1275 cookieable_schemes_.insert(cookieable_schemes_.end(), | |
1276 schemes, schemes + num_schemes); | |
1277 } | |
1278 | |
1279 void CookieMonster::SetKeepExpiredCookies() { | |
1280 keep_expired_cookies_ = true; | |
1281 } | |
1282 | |
1283 void CookieMonster::SetClearPersistentStoreOnExit(bool clear_local_store) { | |
1284 if (store_) | |
1285 store_->SetClearLocalStateOnExit(clear_local_store); | |
1286 } | |
1287 | |
1288 // static | |
1289 void CookieMonster::EnableFileScheme() { | |
1290 enable_file_scheme_ = true; | |
1291 } | |
1292 | |
1293 void CookieMonster::FlushStore(const base::Closure& callback) { | |
1294 base::AutoLock autolock(lock_); | |
1295 if (initialized_ && store_) | |
1296 store_->Flush(callback); | |
1297 else if (!callback.is_null()) | |
1298 MessageLoop::current()->PostTask(FROM_HERE, callback); | |
1299 } | |
1300 | |
1301 bool CookieMonster::SetCookieWithOptions(const GURL& url, | |
1302 const std::string& cookie_line, | |
1303 const CookieOptions& options) { | |
1304 base::AutoLock autolock(lock_); | |
1305 | |
1306 if (!HasCookieableScheme(url)) { | |
1307 return false; | |
1308 } | |
1309 | |
1310 return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options); | |
1311 } | |
1312 | |
1313 std::string CookieMonster::GetCookiesWithOptions(const GURL& url, | |
1314 const CookieOptions& options) { | |
1315 base::AutoLock autolock(lock_); | |
1316 | |
1317 if (!HasCookieableScheme(url)) | |
1318 return std::string(); | |
1319 | |
1320 TimeTicks start_time(TimeTicks::Now()); | |
1321 | |
1322 std::vector<CanonicalCookie*> cookies; | |
1323 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
1324 std::sort(cookies.begin(), cookies.end(), CookieSorter); | |
1325 | |
1326 std::string cookie_line = BuildCookieLine(cookies); | |
1327 | |
1328 histogram_time_get_->AddTime(TimeTicks::Now() - start_time); | |
1329 | |
1330 VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line; | |
1331 | |
1332 return cookie_line; | |
1333 } | |
1334 | |
1335 void CookieMonster::GetCookiesWithInfo(const GURL& url, | |
1336 const CookieOptions& options, | |
1337 std::string* cookie_line, | |
1338 std::vector<CookieInfo>* cookie_infos) { | |
1339 DCHECK(cookie_line->empty()); | |
1340 DCHECK(cookie_infos->empty()); | |
1341 | |
1342 base::AutoLock autolock(lock_); | |
1343 | |
1344 if (!HasCookieableScheme(url)) | |
1345 return; | |
1346 | |
1347 TimeTicks start_time(TimeTicks::Now()); | |
1348 | |
1349 std::vector<CanonicalCookie*> cookies; | |
1350 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
1351 std::sort(cookies.begin(), cookies.end(), CookieSorter); | |
1352 *cookie_line = BuildCookieLine(cookies); | |
1353 | |
1354 histogram_time_get_->AddTime(TimeTicks::Now() - start_time); | |
1355 | |
1356 TimeTicks mac_start_time = TimeTicks::Now(); | |
1357 BuildCookieInfoList(cookies, cookie_infos); | |
1358 histogram_time_mac_->AddTime(TimeTicks::Now() - mac_start_time); | |
1359 } | |
1360 | |
1361 void CookieMonster::DeleteCookie(const GURL& url, | |
1362 const std::string& cookie_name) { | |
1363 base::AutoLock autolock(lock_); | |
1364 | |
1365 if (!HasCookieableScheme(url)) | |
1366 return; | |
1367 | |
1368 CookieOptions options; | |
1369 options.set_include_httponly(); | |
1370 // Get the cookies for this host and its domain(s). | |
1371 std::vector<CanonicalCookie*> cookies; | |
1372 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
1373 std::set<CanonicalCookie*> matching_cookies; | |
1374 | |
1375 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); | |
1376 it != cookies.end(); ++it) { | |
1377 if ((*it)->Name() != cookie_name) | |
1378 continue; | |
1379 if (url.path().find((*it)->Path())) | |
1380 continue; | |
1381 matching_cookies.insert(*it); | |
1382 } | |
1383 | |
1384 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
1385 CookieMap::iterator curit = it; | |
1386 ++it; | |
1387 if (matching_cookies.find(curit->second) != matching_cookies.end()) { | |
1388 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT); | |
1389 } | |
1390 } | |
1391 } | |
1392 | |
1393 CookieMonster* CookieMonster::GetCookieMonster() { | |
1394 return this; | |
1395 } | |
1396 | |
1397 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) { | |
1398 // This function must be called before the CookieMonster is used. | |
1399 DCHECK(!initialized_); | |
1400 persist_session_cookies_ = persist_session_cookies; | |
1401 } | |
1402 | |
1403 void CookieMonster::SaveSessionCookies() { | |
1404 if (store_) { | |
1405 store_->SetClearLocalStateOnExit(false); | |
1406 } | |
1407 } | |
1408 | |
1409 CookieMonster::~CookieMonster() { | |
1410 DeleteAll(false); | |
1411 } | |
1412 | |
1413 bool CookieMonster::SetCookieWithCreationTime(const GURL& url, | |
1414 const std::string& cookie_line, | |
1415 const base::Time& creation_time) { | |
1416 DCHECK(!store_) << "This method is only to be used by unit-tests."; | |
1417 base::AutoLock autolock(lock_); | |
1418 | |
1419 if (!HasCookieableScheme(url)) { | |
1420 return false; | |
1421 } | |
1422 | |
1423 InitIfNecessary(); | |
1424 return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time, | |
1425 CookieOptions()); | |
1426 } | |
1427 | |
1428 void CookieMonster::InitStore() { | |
1429 DCHECK(store_) << "Store must exist to initialize"; | |
1430 | |
1431 // We bind in the current time so that we can report the wall-clock time for | |
1432 // loading cookies. | |
1433 store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now())); | |
1434 } | |
1435 | |
1436 void CookieMonster::OnLoaded(TimeTicks beginning_time, | |
1437 const std::vector<CanonicalCookie*>& cookies) { | |
1438 StoreLoadedCookies(cookies); | |
1439 histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time); | |
1440 | |
1441 // Invoke the task queue of cookie request. | |
1442 InvokeQueue(); | |
1443 } | |
1444 | |
1445 void CookieMonster::OnKeyLoaded(const std::string& key, | |
1446 const std::vector<CanonicalCookie*>& cookies) { | |
1447 // This function does its own separate locking. | |
1448 StoreLoadedCookies(cookies); | |
1449 | |
1450 std::deque<scoped_refptr<CookieMonsterTask> > tasks_queued; | |
1451 { | |
1452 base::AutoLock autolock(lock_); | |
1453 keys_loaded_.insert(key); | |
1454 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > | |
1455 ::iterator it = tasks_queued_.find(key); | |
1456 if (it == tasks_queued_.end()) | |
1457 return; | |
1458 it->second.swap(tasks_queued); | |
1459 tasks_queued_.erase(it); | |
1460 } | |
1461 | |
1462 while (!tasks_queued.empty()) { | |
1463 scoped_refptr<CookieMonsterTask> task = tasks_queued.front(); | |
1464 task->Run(); | |
1465 tasks_queued.pop_front(); | |
1466 } | |
1467 } | |
1468 | |
1469 void CookieMonster::StoreLoadedCookies( | |
1470 const std::vector<CanonicalCookie*>& cookies) { | |
1471 // Initialize the store and sync in any saved persistent cookies. We don't | |
1472 // care if it's expired, insert it so it can be garbage collected, removed, | |
1473 // and sync'd. | |
1474 base::AutoLock autolock(lock_); | |
1475 | |
1476 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); | |
1477 it != cookies.end(); ++it) { | |
1478 int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue(); | |
1479 | |
1480 if (creation_times_.insert(cookie_creation_time).second) { | |
1481 InternalInsertCookie(GetKey((*it)->Domain()), *it, false); | |
1482 const Time cookie_access_time((*it)->LastAccessDate()); | |
1483 if (earliest_access_time_.is_null() || | |
1484 cookie_access_time < earliest_access_time_) | |
1485 earliest_access_time_ = cookie_access_time; | |
1486 } else { | |
1487 LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation " | |
1488 "times in backing store: " | |
1489 "{name='%s', domain='%s', path='%s'}", | |
1490 (*it)->Name().c_str(), | |
1491 (*it)->Domain().c_str(), | |
1492 (*it)->Path().c_str()); | |
1493 // We've been given ownership of the cookie and are throwing it | |
1494 // away; reclaim the space. | |
1495 delete (*it); | |
1496 } | |
1497 } | |
1498 | |
1499 // After importing cookies from the PersistentCookieStore, verify that | |
1500 // none of our other constraints are violated. | |
1501 // In particular, the backing store might have given us duplicate cookies. | |
1502 | |
1503 // This method could be called multiple times due to priority loading, thus | |
1504 // cookies loaded in previous runs will be validated again, but this is OK | |
1505 // since they are expected to be much fewer than total DB. | |
1506 EnsureCookiesMapIsValid(); | |
1507 } | |
1508 | |
1509 void CookieMonster::InvokeQueue() { | |
1510 while (true) { | |
1511 scoped_refptr<CookieMonsterTask> request_task; | |
1512 { | |
1513 base::AutoLock autolock(lock_); | |
1514 if (queue_.empty()) { | |
1515 loaded_ = true; | |
1516 creation_times_.clear(); | |
1517 keys_loaded_.clear(); | |
1518 break; | |
1519 } | |
1520 request_task = queue_.front(); | |
1521 queue_.pop(); | |
1522 } | |
1523 request_task->Run(); | |
1524 } | |
1525 } | |
1526 | |
1527 void CookieMonster::EnsureCookiesMapIsValid() { | |
1528 lock_.AssertAcquired(); | |
1529 | |
1530 int num_duplicates_trimmed = 0; | |
1531 | |
1532 // Iterate through all the of the cookies, grouped by host. | |
1533 CookieMap::iterator prev_range_end = cookies_.begin(); | |
1534 while (prev_range_end != cookies_.end()) { | |
1535 CookieMap::iterator cur_range_begin = prev_range_end; | |
1536 const std::string key = cur_range_begin->first; // Keep a copy. | |
1537 CookieMap::iterator cur_range_end = cookies_.upper_bound(key); | |
1538 prev_range_end = cur_range_end; | |
1539 | |
1540 // Ensure no equivalent cookies for this host. | |
1541 num_duplicates_trimmed += | |
1542 TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end); | |
1543 } | |
1544 | |
1545 // Record how many duplicates were found in the database. | |
1546 // See InitializeHistograms() for details. | |
1547 histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed); | |
1548 } | |
1549 | |
1550 int CookieMonster::TrimDuplicateCookiesForKey( | |
1551 const std::string& key, | |
1552 CookieMap::iterator begin, | |
1553 CookieMap::iterator end) { | |
1554 lock_.AssertAcquired(); | |
1555 | |
1556 // Set of cookies ordered by creation time. | |
1557 typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet; | |
1558 | |
1559 // Helper map we populate to find the duplicates. | |
1560 typedef std::map<CookieSignature, CookieSet> EquivalenceMap; | |
1561 EquivalenceMap equivalent_cookies; | |
1562 | |
1563 // The number of duplicate cookies that have been found. | |
1564 int num_duplicates = 0; | |
1565 | |
1566 // Iterate through all of the cookies in our range, and insert them into | |
1567 // the equivalence map. | |
1568 for (CookieMap::iterator it = begin; it != end; ++it) { | |
1569 DCHECK_EQ(key, it->first); | |
1570 CanonicalCookie* cookie = it->second; | |
1571 | |
1572 CookieSignature signature(cookie->Name(), cookie->Domain(), | |
1573 cookie->Path()); | |
1574 CookieSet& set = equivalent_cookies[signature]; | |
1575 | |
1576 // We found a duplicate! | |
1577 if (!set.empty()) | |
1578 num_duplicates++; | |
1579 | |
1580 // We save the iterator into |cookies_| rather than the actual cookie | |
1581 // pointer, since we may need to delete it later. | |
1582 bool insert_success = set.insert(it).second; | |
1583 DCHECK(insert_success) << | |
1584 "Duplicate creation times found in duplicate cookie name scan."; | |
1585 } | |
1586 | |
1587 // If there were no duplicates, we are done! | |
1588 if (num_duplicates == 0) | |
1589 return 0; | |
1590 | |
1591 // Make sure we find everything below that we did above. | |
1592 int num_duplicates_found = 0; | |
1593 | |
1594 // Otherwise, delete all the duplicate cookies, both from our in-memory store | |
1595 // and from the backing store. | |
1596 for (EquivalenceMap::iterator it = equivalent_cookies.begin(); | |
1597 it != equivalent_cookies.end(); | |
1598 ++it) { | |
1599 const CookieSignature& signature = it->first; | |
1600 CookieSet& dupes = it->second; | |
1601 | |
1602 if (dupes.size() <= 1) | |
1603 continue; // This cookiename/path has no duplicates. | |
1604 num_duplicates_found += dupes.size() - 1; | |
1605 | |
1606 // Since |dups| is sorted by creation time (descending), the first cookie | |
1607 // is the most recent one, so we will keep it. The rest are duplicates. | |
1608 dupes.erase(dupes.begin()); | |
1609 | |
1610 LOG(ERROR) << base::StringPrintf( | |
1611 "Found %d duplicate cookies for host='%s', " | |
1612 "with {name='%s', domain='%s', path='%s'}", | |
1613 static_cast<int>(dupes.size()), | |
1614 key.c_str(), | |
1615 signature.name.c_str(), | |
1616 signature.domain.c_str(), | |
1617 signature.path.c_str()); | |
1618 | |
1619 // Remove all the cookies identified by |dupes|. It is valid to delete our | |
1620 // list of iterators one at a time, since |cookies_| is a multimap (they | |
1621 // don't invalidate existing iterators following deletion). | |
1622 for (CookieSet::iterator dupes_it = dupes.begin(); | |
1623 dupes_it != dupes.end(); | |
1624 ++dupes_it) { | |
1625 InternalDeleteCookie(*dupes_it, true, | |
1626 DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE); | |
1627 } | |
1628 } | |
1629 DCHECK_EQ(num_duplicates, num_duplicates_found); | |
1630 | |
1631 return num_duplicates; | |
1632 } | |
1633 | |
1634 // Note: file must be the last scheme. | |
1635 const char* CookieMonster::kDefaultCookieableSchemes[] = | |
1636 { "http", "https", "file" }; | |
1637 const int CookieMonster::kDefaultCookieableSchemesCount = | |
1638 arraysize(CookieMonster::kDefaultCookieableSchemes); | |
1639 | |
1640 void CookieMonster::SetDefaultCookieableSchemes() { | |
1641 int num_schemes = enable_file_scheme_ ? | |
1642 kDefaultCookieableSchemesCount : kDefaultCookieableSchemesCount - 1; | |
1643 SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes); | |
1644 } | |
1645 | |
1646 | |
1647 void CookieMonster::FindCookiesForHostAndDomain( | |
1648 const GURL& url, | |
1649 const CookieOptions& options, | |
1650 bool update_access_time, | |
1651 std::vector<CanonicalCookie*>* cookies) { | |
1652 lock_.AssertAcquired(); | |
1653 | |
1654 const Time current_time(CurrentTime()); | |
1655 | |
1656 // Probe to save statistics relatively frequently. We do it here rather | |
1657 // than in the set path as many websites won't set cookies, and we | |
1658 // want to collect statistics whenever the browser's being used. | |
1659 RecordPeriodicStats(current_time); | |
1660 | |
1661 // Can just dispatch to FindCookiesForKey | |
1662 const std::string key(GetKey(url.host())); | |
1663 FindCookiesForKey(key, url, options, current_time, | |
1664 update_access_time, cookies); | |
1665 } | |
1666 | |
1667 void CookieMonster::FindCookiesForKey( | |
1668 const std::string& key, | |
1669 const GURL& url, | |
1670 const CookieOptions& options, | |
1671 const Time& current, | |
1672 bool update_access_time, | |
1673 std::vector<CanonicalCookie*>* cookies) { | |
1674 lock_.AssertAcquired(); | |
1675 | |
1676 const std::string scheme(url.scheme()); | |
1677 const std::string host(url.host()); | |
1678 bool secure = url.SchemeIsSecure(); | |
1679 | |
1680 for (CookieMapItPair its = cookies_.equal_range(key); | |
1681 its.first != its.second; ) { | |
1682 CookieMap::iterator curit = its.first; | |
1683 CanonicalCookie* cc = curit->second; | |
1684 ++its.first; | |
1685 | |
1686 // If the cookie is expired, delete it. | |
1687 if (cc->IsExpired(current) && !keep_expired_cookies_) { | |
1688 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED); | |
1689 continue; | |
1690 } | |
1691 | |
1692 // Filter out HttpOnly cookies, per options. | |
1693 if (options.exclude_httponly() && cc->IsHttpOnly()) | |
1694 continue; | |
1695 | |
1696 // Filter out secure cookies unless we're https. | |
1697 if (!secure && cc->IsSecure()) | |
1698 continue; | |
1699 | |
1700 // Filter out cookies that don't apply to this domain. | |
1701 if (!cc->IsDomainMatch(scheme, host)) | |
1702 continue; | |
1703 | |
1704 if (!cc->IsOnPath(url.path())) | |
1705 continue; | |
1706 | |
1707 // Add this cookie to the set of matching cookies. Update the access | |
1708 // time if we've been requested to do so. | |
1709 if (update_access_time) { | |
1710 InternalUpdateCookieAccessTime(cc, current); | |
1711 } | |
1712 cookies->push_back(cc); | |
1713 } | |
1714 } | |
1715 | |
1716 bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key, | |
1717 const CanonicalCookie& ecc, | |
1718 bool skip_httponly, | |
1719 bool already_expired) { | |
1720 lock_.AssertAcquired(); | |
1721 | |
1722 bool found_equivalent_cookie = false; | |
1723 bool skipped_httponly = false; | |
1724 for (CookieMapItPair its = cookies_.equal_range(key); | |
1725 its.first != its.second; ) { | |
1726 CookieMap::iterator curit = its.first; | |
1727 CanonicalCookie* cc = curit->second; | |
1728 ++its.first; | |
1729 | |
1730 if (ecc.IsEquivalent(*cc)) { | |
1731 // We should never have more than one equivalent cookie, since they should | |
1732 // overwrite each other. | |
1733 CHECK(!found_equivalent_cookie) << | |
1734 "Duplicate equivalent cookies found, cookie store is corrupted."; | |
1735 if (skip_httponly && cc->IsHttpOnly()) { | |
1736 skipped_httponly = true; | |
1737 } else { | |
1738 InternalDeleteCookie(curit, true, already_expired ? | |
1739 DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE); | |
1740 } | |
1741 found_equivalent_cookie = true; | |
1742 } | |
1743 } | |
1744 return skipped_httponly; | |
1745 } | |
1746 | |
1747 void CookieMonster::InternalInsertCookie(const std::string& key, | |
1748 CanonicalCookie* cc, | |
1749 bool sync_to_store) { | |
1750 lock_.AssertAcquired(); | |
1751 | |
1752 if ((cc->IsPersistent() || persist_session_cookies_) && | |
1753 store_ && sync_to_store) | |
1754 store_->AddCookie(*cc); | |
1755 cookies_.insert(CookieMap::value_type(key, cc)); | |
1756 if (delegate_.get()) { | |
1757 delegate_->OnCookieChanged( | |
1758 *cc, false, CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT); | |
1759 } | |
1760 } | |
1761 | |
1762 bool CookieMonster::SetCookieWithCreationTimeAndOptions( | |
1763 const GURL& url, | |
1764 const std::string& cookie_line, | |
1765 const Time& creation_time_or_null, | |
1766 const CookieOptions& options) { | |
1767 lock_.AssertAcquired(); | |
1768 | |
1769 VLOG(kVlogSetCookies) << "SetCookie() line: " << cookie_line; | |
1770 | |
1771 Time creation_time = creation_time_or_null; | |
1772 if (creation_time.is_null()) { | |
1773 creation_time = CurrentTime(); | |
1774 last_time_seen_ = creation_time; | |
1775 } | |
1776 | |
1777 // Parse the cookie. | |
1778 ParsedCookie pc(cookie_line); | |
1779 | |
1780 if (!pc.IsValid()) { | |
1781 VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie"; | |
1782 return false; | |
1783 } | |
1784 | |
1785 if (options.exclude_httponly() && pc.IsHttpOnly()) { | |
1786 VLOG(kVlogSetCookies) << "SetCookie() not setting httponly cookie"; | |
1787 return false; | |
1788 } | |
1789 | |
1790 std::string cookie_domain; | |
1791 if (!GetCookieDomain(url, pc, &cookie_domain)) { | |
1792 return false; | |
1793 } | |
1794 | |
1795 std::string cookie_path = CanonPath(url, pc); | |
1796 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); | |
1797 std::string mac_algorithm = pc.HasMACAlgorithm() ? | |
1798 pc.MACAlgorithm() : std::string(); | |
1799 | |
1800 scoped_ptr<CanonicalCookie> cc; | |
1801 Time cookie_expires = CanonExpiration(pc, creation_time); | |
1802 | |
1803 bool session_only = options.force_session() || cookie_expires.is_null(); | |
1804 cc.reset(new CanonicalCookie(url, pc.Name(), pc.Value(), cookie_domain, | |
1805 cookie_path, mac_key, mac_algorithm, | |
1806 creation_time, cookie_expires, | |
1807 creation_time, pc.IsSecure(), pc.IsHttpOnly(), | |
1808 !cookie_expires.is_null(), | |
1809 !session_only)); | |
1810 | |
1811 if (!cc.get()) { | |
1812 VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie"; | |
1813 return false; | |
1814 } | |
1815 return SetCanonicalCookie(&cc, creation_time, options); | |
1816 } | |
1817 | |
1818 bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, | |
1819 const Time& creation_time, | |
1820 const CookieOptions& options) { | |
1821 const std::string key(GetKey((*cc)->Domain())); | |
1822 bool already_expired = (*cc)->IsExpired(creation_time); | |
1823 if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(), | |
1824 already_expired)) { | |
1825 VLOG(kVlogSetCookies) << "SetCookie() not clobbering httponly cookie"; | |
1826 return false; | |
1827 } | |
1828 | |
1829 VLOG(kVlogSetCookies) << "SetCookie() key: " << key << " cc: " | |
1830 << (*cc)->DebugString(); | |
1831 | |
1832 // Realize that we might be setting an expired cookie, and the only point | |
1833 // was to delete the cookie which we've already done. | |
1834 if (!already_expired || keep_expired_cookies_) { | |
1835 // See InitializeHistograms() for details. | |
1836 if ((*cc)->DoesExpire()) { | |
1837 histogram_expiration_duration_minutes_->Add( | |
1838 ((*cc)->ExpiryDate() - creation_time).InMinutes()); | |
1839 } | |
1840 | |
1841 InternalInsertCookie(key, cc->release(), true); | |
1842 } | |
1843 | |
1844 // We assume that hopefully setting a cookie will be less common than | |
1845 // querying a cookie. Since setting a cookie can put us over our limits, | |
1846 // make sure that we garbage collect... We can also make the assumption that | |
1847 // if a cookie was set, in the common case it will be used soon after, | |
1848 // and we will purge the expired cookies in GetCookies(). | |
1849 GarbageCollect(creation_time, key); | |
1850 | |
1851 return true; | |
1852 } | |
1853 | |
1854 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc, | |
1855 const Time& current) { | |
1856 lock_.AssertAcquired(); | |
1857 | |
1858 // Based off the Mozilla code. When a cookie has been accessed recently, | |
1859 // don't bother updating its access time again. This reduces the number of | |
1860 // updates we do during pageload, which in turn reduces the chance our storage | |
1861 // backend will hit its batch thresholds and be forced to update. | |
1862 if ((current - cc->LastAccessDate()) < last_access_threshold_) | |
1863 return; | |
1864 | |
1865 // See InitializeHistograms() for details. | |
1866 histogram_between_access_interval_minutes_->Add( | |
1867 (current - cc->LastAccessDate()).InMinutes()); | |
1868 | |
1869 cc->SetLastAccessDate(current); | |
1870 if ((cc->IsPersistent() || persist_session_cookies_) && store_) | |
1871 store_->UpdateCookieAccessTime(*cc); | |
1872 } | |
1873 | |
1874 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it, | |
1875 bool sync_to_store, | |
1876 DeletionCause deletion_cause) { | |
1877 lock_.AssertAcquired(); | |
1878 | |
1879 // Ideally, this would be asserted up where we define ChangeCauseMapping, | |
1880 // but DeletionCause's visibility (or lack thereof) forces us to make | |
1881 // this check here. | |
1882 COMPILE_ASSERT(arraysize(ChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1, | |
1883 ChangeCauseMapping_size_not_eq_DeletionCause_enum_size); | |
1884 | |
1885 // See InitializeHistograms() for details. | |
1886 if (deletion_cause != DELETE_COOKIE_DONT_RECORD) | |
1887 histogram_cookie_deletion_cause_->Add(deletion_cause); | |
1888 | |
1889 CanonicalCookie* cc = it->second; | |
1890 VLOG(kVlogSetCookies) << "InternalDeleteCookie() cc: " << cc->DebugString(); | |
1891 | |
1892 if ((cc->IsPersistent() || persist_session_cookies_) | |
1893 && store_ && sync_to_store) | |
1894 store_->DeleteCookie(*cc); | |
1895 if (delegate_.get()) { | |
1896 ChangeCausePair mapping = ChangeCauseMapping[deletion_cause]; | |
1897 | |
1898 if (mapping.notify) | |
1899 delegate_->OnCookieChanged(*cc, true, mapping.cause); | |
1900 } | |
1901 cookies_.erase(it); | |
1902 delete cc; | |
1903 } | |
1904 | |
1905 // Domain expiry behavior is unchanged by key/expiry scheme (the | |
1906 // meaning of the key is different, but that's not visible to this | |
1907 // routine). | |
1908 int CookieMonster::GarbageCollect(const Time& current, | |
1909 const std::string& key) { | |
1910 lock_.AssertAcquired(); | |
1911 | |
1912 int num_deleted = 0; | |
1913 | |
1914 // Collect garbage for this key. | |
1915 if (cookies_.count(key) > kDomainMaxCookies) { | |
1916 VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key; | |
1917 | |
1918 std::vector<CookieMap::iterator> cookie_its; | |
1919 num_deleted += GarbageCollectExpired( | |
1920 current, cookies_.equal_range(key), &cookie_its); | |
1921 base::Time oldest_removed; | |
1922 if (FindLeastRecentlyAccessed(kDomainMaxCookies, kDomainPurgeCookies, | |
1923 &oldest_removed, &cookie_its)) { | |
1924 // Delete in two passes so we can figure out what we're nuking | |
1925 // that would be kept at the global level. | |
1926 int num_subject_to_global_purge = | |
1927 GarbageCollectDeleteList( | |
1928 current, | |
1929 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays), | |
1930 DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, | |
1931 cookie_its); | |
1932 num_deleted += num_subject_to_global_purge; | |
1933 // Correct because FindLeastRecentlyAccessed returns a sorted list. | |
1934 cookie_its.erase(cookie_its.begin(), | |
1935 cookie_its.begin() + num_subject_to_global_purge); | |
1936 num_deleted += | |
1937 GarbageCollectDeleteList( | |
1938 current, | |
1939 Time(), | |
1940 DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, | |
1941 cookie_its); | |
1942 } | |
1943 } | |
1944 | |
1945 // Collect garbage for everything. With firefox style we want to | |
1946 // preserve cookies touched in kSafeFromGlobalPurgeDays, otherwise | |
1947 // not. | |
1948 if (cookies_.size() > kMaxCookies && | |
1949 (earliest_access_time_ < | |
1950 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays))) { | |
1951 VLOG(kVlogGarbageCollection) << "GarbageCollect() everything"; | |
1952 std::vector<CookieMap::iterator> cookie_its; | |
1953 base::Time oldest_left; | |
1954 num_deleted += GarbageCollectExpired( | |
1955 current, CookieMapItPair(cookies_.begin(), cookies_.end()), | |
1956 &cookie_its); | |
1957 if (FindLeastRecentlyAccessed(kMaxCookies, kPurgeCookies, | |
1958 &oldest_left, &cookie_its)) { | |
1959 Time oldest_safe_cookie( | |
1960 (Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays))); | |
1961 int num_evicted = GarbageCollectDeleteList( | |
1962 current, | |
1963 oldest_safe_cookie, | |
1964 DELETE_COOKIE_EVICTED_GLOBAL, | |
1965 cookie_its); | |
1966 | |
1967 // If no cookies were preserved by the time limit, the global last | |
1968 // access is set to the value returned from FindLeastRecentlyAccessed. | |
1969 // If the time limit preserved some cookies, we use the last access of | |
1970 // the oldest preserved cookie. | |
1971 if (num_evicted == static_cast<int>(cookie_its.size())) { | |
1972 earliest_access_time_ = oldest_left; | |
1973 } else { | |
1974 earliest_access_time_ = | |
1975 (*(cookie_its.begin() + num_evicted))->second->LastAccessDate(); | |
1976 } | |
1977 num_deleted += num_evicted; | |
1978 } | |
1979 } | |
1980 | |
1981 return num_deleted; | |
1982 } | |
1983 | |
1984 int CookieMonster::GarbageCollectExpired( | |
1985 const Time& current, | |
1986 const CookieMapItPair& itpair, | |
1987 std::vector<CookieMap::iterator>* cookie_its) { | |
1988 if (keep_expired_cookies_) | |
1989 return 0; | |
1990 | |
1991 lock_.AssertAcquired(); | |
1992 | |
1993 int num_deleted = 0; | |
1994 for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) { | |
1995 CookieMap::iterator curit = it; | |
1996 ++it; | |
1997 | |
1998 if (curit->second->IsExpired(current)) { | |
1999 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED); | |
2000 ++num_deleted; | |
2001 } else if (cookie_its) { | |
2002 cookie_its->push_back(curit); | |
2003 } | |
2004 } | |
2005 | |
2006 return num_deleted; | |
2007 } | |
2008 | |
2009 int CookieMonster::GarbageCollectDeleteList( | |
2010 const Time& current, | |
2011 const Time& keep_accessed_after, | |
2012 DeletionCause cause, | |
2013 std::vector<CookieMap::iterator>& cookie_its) { | |
2014 int num_deleted = 0; | |
2015 for (std::vector<CookieMap::iterator>::iterator it = cookie_its.begin(); | |
2016 it != cookie_its.end(); it++) { | |
2017 if (keep_accessed_after.is_null() || | |
2018 (*it)->second->LastAccessDate() < keep_accessed_after) { | |
2019 histogram_evicted_last_access_minutes_->Add( | |
2020 (current - (*it)->second->LastAccessDate()).InMinutes()); | |
2021 InternalDeleteCookie((*it), true, cause); | |
2022 num_deleted++; | |
2023 } | |
2024 } | |
2025 return num_deleted; | |
2026 } | |
2027 | |
2028 // A wrapper around RegistryControlledDomainService::GetDomainAndRegistry | |
2029 // to make clear we're creating a key for our local map. Here and | |
2030 // in FindCookiesForHostAndDomain() are the only two places where | |
2031 // we need to conditionalize based on key type. | |
2032 // | |
2033 // Note that this key algorithm explicitly ignores the scheme. This is | |
2034 // because when we're entering cookies into the map from the backing store, | |
2035 // we in general won't have the scheme at that point. | |
2036 // In practical terms, this means that file cookies will be stored | |
2037 // in the map either by an empty string or by UNC name (and will be | |
2038 // limited by kMaxCookiesPerHost), and extension cookies will be stored | |
2039 // based on the single extension id, as the extension id won't have the | |
2040 // form of a DNS host and hence GetKey() will return it unchanged. | |
2041 // | |
2042 // Arguably the right thing to do here is to make the key | |
2043 // algorithm dependent on the scheme, and make sure that the scheme is | |
2044 // available everywhere the key must be obtained (specfically at backing | |
2045 // store load time). This would require either changing the backing store | |
2046 // database schema to include the scheme (far more trouble than it's worth), or | |
2047 // separating out file cookies into their own CookieMonster instance and | |
2048 // thus restricting each scheme to a single cookie monster (which might | |
2049 // be worth it, but is still too much trouble to solve what is currently a | |
2050 // non-problem). | |
2051 std::string CookieMonster::GetKey(const std::string& domain) const { | |
2052 std::string effective_domain( | |
2053 RegistryControlledDomainService::GetDomainAndRegistry(domain)); | |
2054 if (effective_domain.empty()) | |
2055 effective_domain = domain; | |
2056 | |
2057 if (!effective_domain.empty() && effective_domain[0] == '.') | |
2058 return effective_domain.substr(1); | |
2059 return effective_domain; | |
2060 } | |
2061 | |
2062 bool CookieMonster::HasCookieableScheme(const GURL& url) { | |
2063 lock_.AssertAcquired(); | |
2064 | |
2065 // Make sure the request is on a cookie-able url scheme. | |
2066 for (size_t i = 0; i < cookieable_schemes_.size(); ++i) { | |
2067 // We matched a scheme. | |
2068 if (url.SchemeIs(cookieable_schemes_[i].c_str())) { | |
2069 // We've matched a supported scheme. | |
2070 return true; | |
2071 } | |
2072 } | |
2073 | |
2074 // The scheme didn't match any in our whitelist. | |
2075 VLOG(kVlogPerCookieMonster) << "WARNING: Unsupported cookie scheme: " | |
2076 << url.scheme(); | |
2077 return false; | |
2078 } | |
2079 | |
2080 // Test to see if stats should be recorded, and record them if so. | |
2081 // The goal here is to get sampling for the average browser-hour of | |
2082 // activity. We won't take samples when the web isn't being surfed, | |
2083 // and when the web is being surfed, we'll take samples about every | |
2084 // kRecordStatisticsIntervalSeconds. | |
2085 // last_statistic_record_time_ is initialized to Now() rather than null | |
2086 // in the constructor so that we won't take statistics right after | |
2087 // startup, to avoid bias from browsers that are started but not used. | |
2088 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) { | |
2089 const base::TimeDelta kRecordStatisticsIntervalTime( | |
2090 base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds)); | |
2091 | |
2092 // If we've taken statistics recently, return. | |
2093 if (current_time - last_statistic_record_time_ <= | |
2094 kRecordStatisticsIntervalTime) { | |
2095 return; | |
2096 } | |
2097 | |
2098 // See InitializeHistograms() for details. | |
2099 histogram_count_->Add(cookies_.size()); | |
2100 | |
2101 // More detailed statistics on cookie counts at different granularities. | |
2102 TimeTicks beginning_of_time(TimeTicks::Now()); | |
2103 | |
2104 for (CookieMap::const_iterator it_key = cookies_.begin(); | |
2105 it_key != cookies_.end(); ) { | |
2106 const std::string& key(it_key->first); | |
2107 | |
2108 int key_count = 0; | |
2109 typedef std::map<std::string, unsigned int> DomainMap; | |
2110 DomainMap domain_map; | |
2111 CookieMapItPair its_cookies = cookies_.equal_range(key); | |
2112 while (its_cookies.first != its_cookies.second) { | |
2113 key_count++; | |
2114 const std::string& cookie_domain(its_cookies.first->second->Domain()); | |
2115 domain_map[cookie_domain]++; | |
2116 | |
2117 its_cookies.first++; | |
2118 } | |
2119 histogram_etldp1_count_->Add(key_count); | |
2120 histogram_domain_per_etldp1_count_->Add(domain_map.size()); | |
2121 for (DomainMap::const_iterator domain_map_it = domain_map.begin(); | |
2122 domain_map_it != domain_map.end(); domain_map_it++) | |
2123 histogram_domain_count_->Add(domain_map_it->second); | |
2124 | |
2125 it_key = its_cookies.second; | |
2126 } | |
2127 | |
2128 VLOG(kVlogPeriodic) | |
2129 << "Time for recording cookie stats (us): " | |
2130 << (TimeTicks::Now() - beginning_of_time).InMicroseconds(); | |
2131 | |
2132 last_statistic_record_time_ = current_time; | |
2133 } | |
2134 | |
2135 // Initialize all histogram counter variables used in this class. | |
2136 // | |
2137 // Normal histogram usage involves using the macros defined in | |
2138 // histogram.h, which automatically takes care of declaring these | |
2139 // variables (as statics), initializing them, and accumulating into | |
2140 // them, all from a single entry point. Unfortunately, that solution | |
2141 // doesn't work for the CookieMonster, as it's vulnerable to races between | |
2142 // separate threads executing the same functions and hence initializing the | |
2143 // same static variables. There isn't a race danger in the histogram | |
2144 // accumulation calls; they are written to be resilient to simultaneous | |
2145 // calls from multiple threads. | |
2146 // | |
2147 // The solution taken here is to have per-CookieMonster instance | |
2148 // variables that are constructed during CookieMonster construction. | |
2149 // Note that these variables refer to the same underlying histogram, | |
2150 // so we still race (but safely) with other CookieMonster instances | |
2151 // for accumulation. | |
2152 // | |
2153 // To do this we've expanded out the individual histogram macros calls, | |
2154 // with declarations of the variables in the class decl, initialization here | |
2155 // (done from the class constructor) and direct calls to the accumulation | |
2156 // methods where needed. The specific histogram macro calls on which the | |
2157 // initialization is based are included in comments below. | |
2158 void CookieMonster::InitializeHistograms() { | |
2159 // From UMA_HISTOGRAM_CUSTOM_COUNTS | |
2160 histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet( | |
2161 "Cookie.ExpirationDurationMinutes", | |
2162 1, kMinutesInTenYears, 50, | |
2163 base::Histogram::kUmaTargetedHistogramFlag); | |
2164 histogram_between_access_interval_minutes_ = base::Histogram::FactoryGet( | |
2165 "Cookie.BetweenAccessIntervalMinutes", | |
2166 1, kMinutesInTenYears, 50, | |
2167 base::Histogram::kUmaTargetedHistogramFlag); | |
2168 histogram_evicted_last_access_minutes_ = base::Histogram::FactoryGet( | |
2169 "Cookie.EvictedLastAccessMinutes", | |
2170 1, kMinutesInTenYears, 50, | |
2171 base::Histogram::kUmaTargetedHistogramFlag); | |
2172 histogram_count_ = base::Histogram::FactoryGet( | |
2173 "Cookie.Count", 1, 4000, 50, | |
2174 base::Histogram::kUmaTargetedHistogramFlag); | |
2175 histogram_domain_count_ = base::Histogram::FactoryGet( | |
2176 "Cookie.DomainCount", 1, 4000, 50, | |
2177 base::Histogram::kUmaTargetedHistogramFlag); | |
2178 histogram_etldp1_count_ = base::Histogram::FactoryGet( | |
2179 "Cookie.Etldp1Count", 1, 4000, 50, | |
2180 base::Histogram::kUmaTargetedHistogramFlag); | |
2181 histogram_domain_per_etldp1_count_ = base::Histogram::FactoryGet( | |
2182 "Cookie.DomainPerEtldp1Count", 1, 4000, 50, | |
2183 base::Histogram::kUmaTargetedHistogramFlag); | |
2184 | |
2185 // From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS | |
2186 histogram_number_duplicate_db_cookies_ = base::Histogram::FactoryGet( | |
2187 "Net.NumDuplicateCookiesInDb", 1, 10000, 50, | |
2188 base::Histogram::kUmaTargetedHistogramFlag); | |
2189 | |
2190 // From UMA_HISTOGRAM_ENUMERATION | |
2191 histogram_cookie_deletion_cause_ = base::LinearHistogram::FactoryGet( | |
2192 "Cookie.DeletionCause", 1, | |
2193 DELETE_COOKIE_LAST_ENTRY - 1, DELETE_COOKIE_LAST_ENTRY, | |
2194 base::Histogram::kUmaTargetedHistogramFlag); | |
2195 | |
2196 // From UMA_HISTOGRAM_{CUSTOM_,}TIMES | |
2197 histogram_time_get_ = base::Histogram::FactoryTimeGet("Cookie.TimeGet", | |
2198 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
2199 50, base::Histogram::kUmaTargetedHistogramFlag); | |
2200 histogram_time_mac_ = base::Histogram::FactoryTimeGet("Cookie.TimeGetMac", | |
2201 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
2202 50, base::Histogram::kUmaTargetedHistogramFlag); | |
2203 histogram_time_blocked_on_load_ = base::Histogram::FactoryTimeGet( | |
2204 "Cookie.TimeBlockedOnLoad", | |
2205 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
2206 50, base::Histogram::kUmaTargetedHistogramFlag); | |
2207 } | |
2208 | |
2209 | |
2210 // The system resolution is not high enough, so we can have multiple | |
2211 // set cookies that result in the same system time. When this happens, we | |
2212 // increment by one Time unit. Let's hope computers don't get too fast. | |
2213 Time CookieMonster::CurrentTime() { | |
2214 return std::max(Time::Now(), | |
2215 Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1)); | |
2216 } | |
2217 | |
2218 CookieMonster::ParsedCookie::ParsedCookie(const std::string& cookie_line) | |
2219 : is_valid_(false), | |
2220 path_index_(0), | |
2221 domain_index_(0), | |
2222 mac_key_index_(0), | |
2223 mac_algorithm_index_(0), | |
2224 expires_index_(0), | |
2225 maxage_index_(0), | |
2226 secure_index_(0), | |
2227 httponly_index_(0) { | |
2228 | |
2229 if (cookie_line.size() > kMaxCookieSize) { | |
2230 VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size(); | |
2231 return; | |
2232 } | |
2233 | |
2234 ParseTokenValuePairs(cookie_line); | |
2235 if (!pairs_.empty()) { | |
2236 is_valid_ = true; | |
2237 SetupAttributes(); | |
2238 } | |
2239 } | |
2240 | |
2241 CookieMonster::ParsedCookie::~ParsedCookie() { | |
2242 } | |
2243 | |
2244 // Returns true if |c| occurs in |chars| | |
2245 // TODO(erikwright): maybe make this take an iterator, could check for end also? | |
2246 static inline bool CharIsA(const char c, const char* chars) { | |
2247 return strchr(chars, c) != NULL; | |
2248 } | |
2249 // Seek the iterator to the first occurrence of a character in |chars|. | |
2250 // Returns true if it hit the end, false otherwise. | |
2251 static inline bool SeekTo(std::string::const_iterator* it, | |
2252 const std::string::const_iterator& end, | |
2253 const char* chars) { | |
2254 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {} | |
2255 return *it == end; | |
2256 } | |
2257 // Seek the iterator to the first occurrence of a character not in |chars|. | |
2258 // Returns true if it hit the end, false otherwise. | |
2259 static inline bool SeekPast(std::string::const_iterator* it, | |
2260 const std::string::const_iterator& end, | |
2261 const char* chars) { | |
2262 for (; *it != end && CharIsA(**it, chars); ++(*it)) {} | |
2263 return *it == end; | |
2264 } | |
2265 static inline bool SeekBackPast(std::string::const_iterator* it, | |
2266 const std::string::const_iterator& end, | |
2267 const char* chars) { | |
2268 for (; *it != end && CharIsA(**it, chars); --(*it)) {} | |
2269 return *it == end; | |
2270 } | |
2271 | |
2272 const char CookieMonster::ParsedCookie::kTerminator[] = "\n\r\0"; | |
2273 const int CookieMonster::ParsedCookie::kTerminatorLen = | |
2274 sizeof(kTerminator) - 1; | |
2275 const char CookieMonster::ParsedCookie::kWhitespace[] = " \t"; | |
2276 const char CookieMonster::ParsedCookie::kValueSeparator[] = ";"; | |
2277 const char CookieMonster::ParsedCookie::kTokenSeparator[] = ";="; | |
2278 | |
2279 // Create a cookie-line for the cookie. For debugging only! | |
2280 // If we want to use this for something more than debugging, we | |
2281 // should rewrite it better... | |
2282 std::string CookieMonster::ParsedCookie::DebugString() const { | |
2283 std::string out; | |
2284 for (PairList::const_iterator it = pairs_.begin(); | |
2285 it != pairs_.end(); ++it) { | |
2286 out.append(it->first); | |
2287 out.append("="); | |
2288 out.append(it->second); | |
2289 out.append("; "); | |
2290 } | |
2291 return out; | |
2292 } | |
2293 | |
2294 std::string::const_iterator CookieMonster::ParsedCookie::FindFirstTerminator( | |
2295 const std::string& s) { | |
2296 std::string::const_iterator end = s.end(); | |
2297 size_t term_pos = | |
2298 s.find_first_of(std::string(kTerminator, kTerminatorLen)); | |
2299 if (term_pos != std::string::npos) { | |
2300 // We found a character we should treat as an end of string. | |
2301 end = s.begin() + term_pos; | |
2302 } | |
2303 return end; | |
2304 } | |
2305 | |
2306 bool CookieMonster::ParsedCookie::ParseToken( | |
2307 std::string::const_iterator* it, | |
2308 const std::string::const_iterator& end, | |
2309 std::string::const_iterator* token_start, | |
2310 std::string::const_iterator* token_end) { | |
2311 DCHECK(it && token_start && token_end); | |
2312 std::string::const_iterator token_real_end; | |
2313 | |
2314 // Seek past any whitespace before the "token" (the name). | |
2315 // token_start should point at the first character in the token | |
2316 if (SeekPast(it, end, kWhitespace)) | |
2317 return false; // No token, whitespace or empty. | |
2318 *token_start = *it; | |
2319 | |
2320 // Seek over the token, to the token separator. | |
2321 // token_real_end should point at the token separator, i.e. '='. | |
2322 // If it == end after the seek, we probably have a token-value. | |
2323 SeekTo(it, end, kTokenSeparator); | |
2324 token_real_end = *it; | |
2325 | |
2326 // Ignore any whitespace between the token and the token separator. | |
2327 // token_end should point after the last interesting token character, | |
2328 // pointing at either whitespace, or at '=' (and equal to token_real_end). | |
2329 if (*it != *token_start) { // We could have an empty token name. | |
2330 --(*it); // Go back before the token separator. | |
2331 // Skip over any whitespace to the first non-whitespace character. | |
2332 SeekBackPast(it, *token_start, kWhitespace); | |
2333 // Point after it. | |
2334 ++(*it); | |
2335 } | |
2336 *token_end = *it; | |
2337 | |
2338 // Seek us back to the end of the token. | |
2339 *it = token_real_end; | |
2340 return true; | |
2341 } | |
2342 | |
2343 void CookieMonster::ParsedCookie::ParseValue( | |
2344 std::string::const_iterator* it, | |
2345 const std::string::const_iterator& end, | |
2346 std::string::const_iterator* value_start, | |
2347 std::string::const_iterator* value_end) { | |
2348 DCHECK(it && value_start && value_end); | |
2349 | |
2350 // Seek past any whitespace that might in-between the token and value. | |
2351 SeekPast(it, end, kWhitespace); | |
2352 // value_start should point at the first character of the value. | |
2353 *value_start = *it; | |
2354 | |
2355 // Just look for ';' to terminate ('=' allowed). | |
2356 // We can hit the end, maybe they didn't terminate. | |
2357 SeekTo(it, end, kValueSeparator); | |
2358 | |
2359 // Will be pointed at the ; seperator or the end. | |
2360 *value_end = *it; | |
2361 | |
2362 // Ignore any unwanted whitespace after the value. | |
2363 if (*value_end != *value_start) { // Could have an empty value | |
2364 --(*value_end); | |
2365 SeekBackPast(value_end, *value_start, kWhitespace); | |
2366 ++(*value_end); | |
2367 } | |
2368 } | |
2369 | |
2370 std::string CookieMonster::ParsedCookie::ParseTokenString( | |
2371 const std::string& token) { | |
2372 std::string::const_iterator it = token.begin(); | |
2373 std::string::const_iterator end = FindFirstTerminator(token); | |
2374 | |
2375 std::string::const_iterator token_start, token_end; | |
2376 if (ParseToken(&it, end, &token_start, &token_end)) | |
2377 return std::string(token_start, token_end); | |
2378 return std::string(); | |
2379 } | |
2380 | |
2381 std::string CookieMonster::ParsedCookie::ParseValueString( | |
2382 const std::string& value) { | |
2383 std::string::const_iterator it = value.begin(); | |
2384 std::string::const_iterator end = FindFirstTerminator(value); | |
2385 | |
2386 std::string::const_iterator value_start, value_end; | |
2387 ParseValue(&it, end, &value_start, &value_end); | |
2388 return std::string(value_start, value_end); | |
2389 } | |
2390 | |
2391 // Parse all token/value pairs and populate pairs_. | |
2392 void CookieMonster::ParsedCookie::ParseTokenValuePairs( | |
2393 const std::string& cookie_line) { | |
2394 pairs_.clear(); | |
2395 | |
2396 // Ok, here we go. We should be expecting to be starting somewhere | |
2397 // before the cookie line, not including any header name... | |
2398 std::string::const_iterator start = cookie_line.begin(); | |
2399 std::string::const_iterator it = start; | |
2400 | |
2401 // TODO(erikwright): Make sure we're stripping \r\n in the network code. | |
2402 // Then we can log any unexpected terminators. | |
2403 std::string::const_iterator end = FindFirstTerminator(cookie_line); | |
2404 | |
2405 for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) { | |
2406 TokenValuePair pair; | |
2407 | |
2408 std::string::const_iterator token_start, token_end; | |
2409 if (!ParseToken(&it, end, &token_start, &token_end)) | |
2410 break; | |
2411 | |
2412 if (it == end || *it != '=') { | |
2413 // We have a token-value, we didn't have any token name. | |
2414 if (pair_num == 0) { | |
2415 // For the first time around, we want to treat single values | |
2416 // as a value with an empty name. (Mozilla bug 169091). | |
2417 // IE seems to also have this behavior, ex "AAA", and "AAA=10" will | |
2418 // set 2 different cookies, and setting "BBB" will then replace "AAA". | |
2419 pair.first = ""; | |
2420 // Rewind to the beginning of what we thought was the token name, | |
2421 // and let it get parsed as a value. | |
2422 it = token_start; | |
2423 } else { | |
2424 // Any not-first attribute we want to treat a value as a | |
2425 // name with an empty value... This is so something like | |
2426 // "secure;" will get parsed as a Token name, and not a value. | |
2427 pair.first = std::string(token_start, token_end); | |
2428 } | |
2429 } else { | |
2430 // We have a TOKEN=VALUE. | |
2431 pair.first = std::string(token_start, token_end); | |
2432 ++it; // Skip past the '='. | |
2433 } | |
2434 | |
2435 // OK, now try to parse a value. | |
2436 std::string::const_iterator value_start, value_end; | |
2437 ParseValue(&it, end, &value_start, &value_end); | |
2438 // OK, we're finished with a Token/Value. | |
2439 pair.second = std::string(value_start, value_end); | |
2440 | |
2441 // From RFC2109: "Attributes (names) (attr) are case-insensitive." | |
2442 if (pair_num != 0) | |
2443 StringToLowerASCII(&pair.first); | |
2444 pairs_.push_back(pair); | |
2445 | |
2446 // We've processed a token/value pair, we're either at the end of | |
2447 // the string or a ValueSeparator like ';', which we want to skip. | |
2448 if (it != end) | |
2449 ++it; | |
2450 } | |
2451 } | |
2452 | |
2453 void CookieMonster::ParsedCookie::SetupAttributes() { | |
2454 static const char kPathTokenName[] = "path"; | |
2455 static const char kDomainTokenName[] = "domain"; | |
2456 static const char kMACKeyTokenName[] = "mac-key"; | |
2457 static const char kMACAlgorithmTokenName[] = "mac-algorithm"; | |
2458 static const char kExpiresTokenName[] = "expires"; | |
2459 static const char kMaxAgeTokenName[] = "max-age"; | |
2460 static const char kSecureTokenName[] = "secure"; | |
2461 static const char kHttpOnlyTokenName[] = "httponly"; | |
2462 | |
2463 // We skip over the first token/value, the user supplied one. | |
2464 for (size_t i = 1; i < pairs_.size(); ++i) { | |
2465 if (pairs_[i].first == kPathTokenName) { | |
2466 path_index_ = i; | |
2467 } else if (pairs_[i].first == kDomainTokenName) { | |
2468 domain_index_ = i; | |
2469 } else if (pairs_[i].first == kMACKeyTokenName) { | |
2470 mac_key_index_ = i; | |
2471 } else if (pairs_[i].first == kMACAlgorithmTokenName) { | |
2472 mac_algorithm_index_ = i; | |
2473 } else if (pairs_[i].first == kExpiresTokenName) { | |
2474 expires_index_ = i; | |
2475 } else if (pairs_[i].first == kMaxAgeTokenName) { | |
2476 maxage_index_ = i; | |
2477 } else if (pairs_[i].first == kSecureTokenName) { | |
2478 secure_index_ = i; | |
2479 } else if (pairs_[i].first == kHttpOnlyTokenName) { | |
2480 httponly_index_ = i; | |
2481 } else { | |
2482 /* some attribute we don't know or don't care about. */ | |
2483 } | |
2484 } | |
2485 } | |
2486 | |
2487 CookieMonster::CanonicalCookie::CanonicalCookie() | |
2488 : secure_(false), | |
2489 httponly_(false), | |
2490 has_expires_(false), | |
2491 is_persistent_(false) { | |
2492 SetSessionCookieExpiryTime(); | |
2493 } | |
2494 | |
2495 CookieMonster::CanonicalCookie::CanonicalCookie( | |
2496 const GURL& url, const std::string& name, const std::string& value, | |
2497 const std::string& domain, const std::string& path, | |
2498 const std::string& mac_key, const std::string& mac_algorithm, | |
2499 const base::Time& creation, const base::Time& expiration, | |
2500 const base::Time& last_access, bool secure, bool httponly, bool has_expires, | |
2501 bool is_persistent) | |
2502 : source_(GetCookieSourceFromURL(url)), | |
2503 name_(name), | |
2504 value_(value), | |
2505 domain_(domain), | |
2506 path_(path), | |
2507 mac_key_(mac_key), | |
2508 mac_algorithm_(mac_algorithm), | |
2509 creation_date_(creation), | |
2510 expiry_date_(expiration), | |
2511 last_access_date_(last_access), | |
2512 secure_(secure), | |
2513 httponly_(httponly), | |
2514 has_expires_(has_expires), | |
2515 is_persistent_(is_persistent) { | |
2516 if (!has_expires_) { | |
2517 DCHECK(!is_persistent_); | |
2518 SetSessionCookieExpiryTime(); | |
2519 } | |
2520 } | |
2521 | |
2522 CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url, | |
2523 const ParsedCookie& pc) | |
2524 : source_(GetCookieSourceFromURL(url)), | |
2525 name_(pc.Name()), | |
2526 value_(pc.Value()), | |
2527 path_(CanonPath(url, pc)), | |
2528 mac_key_(pc.MACKey()), | |
2529 mac_algorithm_(pc.MACAlgorithm()), | |
2530 creation_date_(Time::Now()), | |
2531 last_access_date_(Time()), | |
2532 secure_(pc.IsSecure()), | |
2533 httponly_(pc.IsHttpOnly()), | |
2534 has_expires_(pc.HasExpires()), | |
2535 is_persistent_(pc.HasExpires()) { | |
2536 if (has_expires_) | |
2537 expiry_date_ = CanonExpiration(pc, creation_date_); | |
2538 else | |
2539 SetSessionCookieExpiryTime(); | |
2540 | |
2541 // Do the best we can with the domain. | |
2542 std::string cookie_domain; | |
2543 std::string domain_string; | |
2544 if (pc.HasDomain()) { | |
2545 domain_string = pc.Domain(); | |
2546 } | |
2547 bool result | |
2548 = cookie_util::GetCookieDomainWithString(url, domain_string, | |
2549 &cookie_domain); | |
2550 // Caller is responsible for passing in good arguments. | |
2551 DCHECK(result); | |
2552 domain_ = cookie_domain; | |
2553 } | |
2554 | |
2555 CookieMonster::CanonicalCookie::~CanonicalCookie() { | |
2556 } | |
2557 | |
2558 std::string CookieMonster::CanonicalCookie::GetCookieSourceFromURL( | |
2559 const GURL& url) { | |
2560 if (url.SchemeIsFile()) | |
2561 return url.spec(); | |
2562 | |
2563 url_canon::Replacements<char> replacements; | |
2564 replacements.ClearPort(); | |
2565 if (url.SchemeIsSecure()) | |
2566 replacements.SetScheme("http", url_parse::Component(0, 4)); | |
2567 | |
2568 return url.GetOrigin().ReplaceComponents(replacements).spec(); | |
2569 } | |
2570 | |
2571 void CookieMonster::CanonicalCookie::SetSessionCookieExpiryTime() { | |
2572 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
2573 // Mobile apps can sometimes be shut down without any warning, so the session | |
2574 // cookie has to be persistent and given a default expiration time. | |
2575 expiry_date_ = base::Time::Now() + | |
2576 base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays); | |
2577 has_expires_ = true; | |
2578 #endif | |
2579 } | |
2580 | |
2581 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create( | |
2582 const GURL& url, | |
2583 const ParsedCookie& pc) { | |
2584 if (!pc.IsValid()) { | |
2585 return NULL; | |
2586 } | |
2587 | |
2588 std::string domain_string; | |
2589 if (!GetCookieDomain(url, pc, &domain_string)) { | |
2590 return NULL; | |
2591 } | |
2592 std::string path_string = CanonPath(url, pc); | |
2593 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); | |
2594 std::string mac_algorithm = pc.HasMACAlgorithm() ? | |
2595 pc.MACAlgorithm() : std::string(); | |
2596 Time creation_time = Time::Now(); | |
2597 Time expiration_time; | |
2598 if (pc.HasExpires()) | |
2599 expiration_time = net::CookieMonster::ParseCookieTime(pc.Expires()); | |
2600 | |
2601 return (Create(url, pc.Name(), pc.Value(), domain_string, path_string, | |
2602 mac_key, mac_algorithm, creation_time, expiration_time, | |
2603 pc.IsSecure(), pc.IsHttpOnly(), !expiration_time.is_null())); | |
2604 } | |
2605 | |
2606 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create( | |
2607 const GURL& url, | |
2608 const std::string& name, | |
2609 const std::string& value, | |
2610 const std::string& domain, | |
2611 const std::string& path, | |
2612 const std::string& mac_key, | |
2613 const std::string& mac_algorithm, | |
2614 const base::Time& creation, | |
2615 const base::Time& expiration, | |
2616 bool secure, | |
2617 bool http_only, | |
2618 bool is_persistent) { | |
2619 // Expect valid attribute tokens and values, as defined by the ParsedCookie | |
2620 // logic, otherwise don't create the cookie. | |
2621 std::string parsed_name = ParsedCookie::ParseTokenString(name); | |
2622 if (parsed_name != name) | |
2623 return NULL; | |
2624 std::string parsed_value = ParsedCookie::ParseValueString(value); | |
2625 if (parsed_value != value) | |
2626 return NULL; | |
2627 | |
2628 std::string parsed_domain = ParsedCookie::ParseValueString(domain); | |
2629 if (parsed_domain != domain) | |
2630 return NULL; | |
2631 std::string cookie_domain; | |
2632 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, | |
2633 &cookie_domain)) { | |
2634 return NULL; | |
2635 } | |
2636 | |
2637 std::string parsed_path = ParsedCookie::ParseValueString(path); | |
2638 if (parsed_path != path) | |
2639 return NULL; | |
2640 | |
2641 std::string cookie_path = CanonPathWithString(url, parsed_path); | |
2642 // Expect that the path was either not specified (empty), or is valid. | |
2643 if (!parsed_path.empty() && cookie_path != parsed_path) | |
2644 return NULL; | |
2645 // Canonicalize path again to make sure it escapes characters as needed. | |
2646 url_parse::Component path_component(0, cookie_path.length()); | |
2647 url_canon::RawCanonOutputT<char> canon_path; | |
2648 url_parse::Component canon_path_component; | |
2649 url_canon::CanonicalizePath(cookie_path.data(), path_component, | |
2650 &canon_path, &canon_path_component); | |
2651 cookie_path = std::string(canon_path.data() + canon_path_component.begin, | |
2652 canon_path_component.len); | |
2653 | |
2654 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, | |
2655 cookie_path, mac_key, mac_algorithm, creation, | |
2656 expiration, creation, secure, http_only, | |
2657 !expiration.is_null(), is_persistent); | |
2658 } | |
2659 | |
2660 bool CookieMonster::CanonicalCookie::IsOnPath( | |
2661 const std::string& url_path) const { | |
2662 | |
2663 // A zero length would be unsafe for our trailing '/' checks, and | |
2664 // would also make no sense for our prefix match. The code that | |
2665 // creates a CanonicalCookie should make sure the path is never zero length, | |
2666 // but we double check anyway. | |
2667 if (path_.empty()) | |
2668 return false; | |
2669 | |
2670 // The Mozilla code broke it into 3 cases, if it's strings lengths | |
2671 // are less than, equal, or greater. I think this is simpler: | |
2672 | |
2673 // Make sure the cookie path is a prefix of the url path. If the | |
2674 // url path is shorter than the cookie path, then the cookie path | |
2675 // can't be a prefix. | |
2676 if (url_path.find(path_) != 0) | |
2677 return false; | |
2678 | |
2679 // Now we know that url_path is >= cookie_path, and that cookie_path | |
2680 // is a prefix of url_path. If they are the are the same length then | |
2681 // they are identical, otherwise we need an additional check: | |
2682 | |
2683 // In order to avoid in correctly matching a cookie path of /blah | |
2684 // with a request path of '/blahblah/', we need to make sure that either | |
2685 // the cookie path ends in a trailing '/', or that we prefix up to a '/' | |
2686 // in the url path. Since we know that the url path length is greater | |
2687 // than the cookie path length, it's safe to index one byte past. | |
2688 if (path_.length() != url_path.length() && | |
2689 path_[path_.length() - 1] != '/' && | |
2690 url_path[path_.length()] != '/') | |
2691 return false; | |
2692 | |
2693 return true; | |
2694 } | |
2695 | |
2696 bool CookieMonster::CanonicalCookie::IsDomainMatch( | |
2697 const std::string& scheme, | |
2698 const std::string& host) const { | |
2699 // Can domain match in two ways; as a domain cookie (where the cookie | |
2700 // domain begins with ".") or as a host cookie (where it doesn't). | |
2701 | |
2702 // Some consumers of the CookieMonster expect to set cookies on | |
2703 // URLs like http://.strange.url. To retrieve cookies in this instance, | |
2704 // we allow matching as a host cookie even when the domain_ starts with | |
2705 // a period. | |
2706 if (host == domain_) | |
2707 return true; | |
2708 | |
2709 // Domain cookie must have an initial ".". To match, it must be | |
2710 // equal to url's host with initial period removed, or a suffix of | |
2711 // it. | |
2712 | |
2713 // Arguably this should only apply to "http" or "https" cookies, but | |
2714 // extension cookie tests currently use the funtionality, and if we | |
2715 // ever decide to implement that it should be done by preventing | |
2716 // such cookies from being set. | |
2717 if (domain_.empty() || domain_[0] != '.') | |
2718 return false; | |
2719 | |
2720 // The host with a "." prefixed. | |
2721 if (domain_.compare(1, std::string::npos, host) == 0) | |
2722 return true; | |
2723 | |
2724 // A pure suffix of the host (ok since we know the domain already | |
2725 // starts with a ".") | |
2726 return (host.length() > domain_.length() && | |
2727 host.compare(host.length() - domain_.length(), | |
2728 domain_.length(), domain_) == 0); | |
2729 } | |
2730 | |
2731 std::string CookieMonster::CanonicalCookie::DebugString() const { | |
2732 return base::StringPrintf( | |
2733 "name: %s value: %s domain: %s path: %s creation: %" | |
2734 PRId64, | |
2735 name_.c_str(), value_.c_str(), | |
2736 domain_.c_str(), path_.c_str(), | |
2737 static_cast<int64>(creation_date_.ToTimeT())); | |
2738 } | |
2739 | |
2740 } // namespace | |
OLD | NEW |