xref: /aosp_15_r20/external/cronet/net/cookies/cookie_monster.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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 ([email protected])
29  *   Michiel van Leeuwen ([email protected])
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/cookies/cookie_monster.h"
46 
47 #include <functional>
48 #include <list>
49 #include <numeric>
50 #include <optional>
51 #include <set>
52 #include <string_view>
53 #include <utility>
54 
55 #include "base/check_is_test.h"
56 #include "base/containers/flat_map.h"
57 #include "base/feature_list.h"
58 #include "base/functional/bind.h"
59 #include "base/functional/callback.h"
60 #include "base/location.h"
61 #include "base/logging.h"
62 #include "base/metrics/field_trial.h"
63 #include "base/metrics/histogram_functions.h"
64 #include "base/metrics/histogram_macros.h"
65 #include "base/ranges/algorithm.h"
66 #include "base/strings/strcat.h"
67 #include "base/strings/string_util.h"
68 #include "base/strings/stringprintf.h"
69 #include "base/task/single_thread_task_runner.h"
70 #include "base/threading/thread_checker.h"
71 #include "base/time/time.h"
72 #include "net/base/isolation_info.h"
73 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
74 #include "net/base/schemeful_site.h"
75 #include "net/base/url_util.h"
76 #include "net/cookies/canonical_cookie.h"
77 #include "net/cookies/cookie_constants.h"
78 #include "net/cookies/cookie_monster_change_dispatcher.h"
79 #include "net/cookies/cookie_monster_netlog_params.h"
80 #include "net/cookies/cookie_partition_key.h"
81 #include "net/cookies/cookie_partition_key_collection.h"
82 #include "net/cookies/cookie_util.h"
83 #include "net/cookies/parsed_cookie.h"
84 #include "net/http/http_util.h"
85 #include "net/log/net_log.h"
86 #include "net/log/net_log_values.h"
87 #include "url/origin.h"
88 #include "url/third_party/mozilla/url_parse.h"
89 #include "url/url_canon.h"
90 #include "url/url_constants.h"
91 
92 using base::Time;
93 using base::TimeTicks;
94 using TimeRange = net::CookieDeletionInfo::TimeRange;
95 
96 // In steady state, most cookie requests can be satisfied by the in memory
97 // cookie monster store. If the cookie request cannot be satisfied by the in
98 // memory store, the relevant cookies must be fetched from the persistent
99 // store. The task is queued in CookieMonster::tasks_pending_ if it requires
100 // all cookies to be loaded from the backend, or tasks_pending_for_key_ if it
101 // only requires all cookies associated with an eTLD+1.
102 //
103 // On the browser critical paths (e.g. for loading initial web pages in a
104 // session restore) it may take too long to wait for the full load. If a cookie
105 // request is for a specific URL, DoCookieCallbackForURL is called, which
106 // triggers a priority load if the key is not loaded yet by calling
107 // PersistentCookieStore::LoadCookiesForKey. The request is queued in
108 // CookieMonster::tasks_pending_for_key_ and executed upon receiving
109 // notification of key load completion via CookieMonster::OnKeyLoaded(). If
110 // multiple requests for the same eTLD+1 are received before key load
111 // completion, only the first request calls
112 // PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued
113 // in CookieMonster::tasks_pending_for_key_ and executed upon receiving
114 // notification of key load completion triggered by the first request for the
115 // same eTLD+1.
116 
117 static const int kDaysInTenYears = 10 * 365;
118 static const int kMinutesInTenYears = kDaysInTenYears * 24 * 60;
119 
120 namespace {
121 
122 // This enum is used to generate a histogramed bitmask measureing the types
123 // of stored cookies. Please do not reorder the list when adding new entries.
124 // New items MUST be added at the end of the list, just before
125 // COOKIE_TYPE_LAST_ENTRY;
126 // There will be 2^COOKIE_TYPE_LAST_ENTRY buckets in the linear histogram.
127 enum CookieType {
128   COOKIE_TYPE_SAME_SITE = 0,
129   COOKIE_TYPE_HTTPONLY,
130   COOKIE_TYPE_SECURE,
131   COOKIE_TYPE_PERSISTENT,
132   COOKIE_TYPE_LAST_ENTRY
133 };
134 
MaybeRunDeleteCallback(base::WeakPtr<net::CookieMonster> cookie_monster,base::OnceClosure callback)135 void MaybeRunDeleteCallback(base::WeakPtr<net::CookieMonster> cookie_monster,
136                             base::OnceClosure callback) {
137   if (cookie_monster && callback)
138     std::move(callback).Run();
139 }
140 
141 template <typename CB, typename... R>
MaybeRunCookieCallback(base::OnceCallback<CB> callback,R &&...result)142 void MaybeRunCookieCallback(base::OnceCallback<CB> callback, R&&... result) {
143   if (callback) {
144     std::move(callback).Run(std::forward<R>(result)...);
145   }
146 }
147 
148 // Anonymous and Fenced Frame uses a CookiePartitionKey with a nonce. In these
149 // contexts, access to unpartitioned cookie is not granted.
150 //
151 // This returns true if the |list| of key should include unpartitioned cookie in
152 // GetCookie...().
IncludeUnpartitionedCookies(const net::CookiePartitionKeyCollection & list)153 bool IncludeUnpartitionedCookies(
154     const net::CookiePartitionKeyCollection& list) {
155   if (list.IsEmpty() || list.ContainsAllKeys())
156     return true;
157 
158   for (const net::CookiePartitionKey& key : list.PartitionKeys()) {
159     if (!key.nonce())
160       return true;
161   }
162   return false;
163 }
164 
NameValueSizeBytes(const net::CanonicalCookie & cc)165 size_t NameValueSizeBytes(const net::CanonicalCookie& cc) {
166   base::CheckedNumeric<size_t> name_value_pair_size = cc.Name().size();
167   name_value_pair_size += cc.Value().size();
168   DCHECK(name_value_pair_size.IsValid());
169   return name_value_pair_size.ValueOrDie();
170 }
171 
NumBytesInCookieMapForKey(const net::CookieMonster::CookieMap & cookie_map,const std::string & key)172 size_t NumBytesInCookieMapForKey(
173     const net::CookieMonster::CookieMap& cookie_map,
174     const std::string& key) {
175   size_t result = 0;
176   auto range = cookie_map.equal_range(key);
177   for (auto it = range.first; it != range.second; ++it) {
178     result += NameValueSizeBytes(*it->second);
179   }
180   return result;
181 }
182 
NumBytesInCookieItVector(const net::CookieMonster::CookieItVector & cookie_its)183 size_t NumBytesInCookieItVector(
184     const net::CookieMonster::CookieItVector& cookie_its) {
185   size_t result = 0;
186   for (const auto& it : cookie_its) {
187     result += NameValueSizeBytes(*it->second);
188   }
189   return result;
190 }
191 
LogStoredCookieToUMA(const net::CanonicalCookie & cc,const net::CookieAccessResult & access_result)192 void LogStoredCookieToUMA(const net::CanonicalCookie& cc,
193                           const net::CookieAccessResult& access_result) {
194   // Cookie.Type2 collects a bitvector of important cookie attributes.
195   int32_t type_sample =
196       !cc.IsEffectivelySameSiteNone(access_result.access_semantics)
197           ? 1 << COOKIE_TYPE_SAME_SITE
198           : 0;
199   type_sample |= cc.IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
200   type_sample |= cc.SecureAttribute() ? 1 << COOKIE_TYPE_SECURE : 0;
201   type_sample |= cc.IsPersistent() ? 1 << COOKIE_TYPE_PERSISTENT : 0;
202   UMA_HISTOGRAM_EXACT_LINEAR("Cookie.Type2", type_sample,
203                              (1 << COOKIE_TYPE_LAST_ENTRY));
204 
205   // Cookie.SourceType collects the CookieSourceType of the stored cookie.
206   UMA_HISTOGRAM_ENUMERATION("Cookie.SourceType", cc.SourceType());
207 }
208 
209 }  // namespace
210 
211 namespace net {
212 
213 // See comments at declaration of these variables in cookie_monster.h
214 // for details.
215 const size_t CookieMonster::kDomainMaxCookies = 180;
216 const size_t CookieMonster::kDomainPurgeCookies = 30;
217 const size_t CookieMonster::kMaxCookies = 3300;
218 const size_t CookieMonster::kPurgeCookies = 300;
219 
220 const size_t CookieMonster::kMaxDomainPurgedKeys = 100;
221 
222 const size_t CookieMonster::kPerPartitionDomainMaxCookieBytes = 10240;
223 const size_t CookieMonster::kPerPartitionDomainMaxCookies = 180;
224 
225 const size_t CookieMonster::kDomainCookiesQuotaLow = 30;
226 const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
227 const size_t CookieMonster::kDomainCookiesQuotaHigh =
228     kDomainMaxCookies - kDomainPurgeCookies - kDomainCookiesQuotaLow -
229     kDomainCookiesQuotaMedium;
230 
231 const int CookieMonster::kSafeFromGlobalPurgeDays = 30;
232 
233 namespace {
234 
ContainsControlCharacter(const std::string & s)235 bool ContainsControlCharacter(const std::string& s) {
236   return base::ranges::any_of(s, &HttpUtil::IsControlChar);
237 }
238 
239 typedef std::vector<CanonicalCookie*> CanonicalCookieVector;
240 
241 // Default minimum delay after updating a cookie's LastAccessDate before we
242 // will update it again.
243 const int kDefaultAccessUpdateThresholdSeconds = 60;
244 
245 // Comparator to sort cookies from highest creation date to lowest
246 // creation date.
247 struct OrderByCreationTimeDesc {
operator ()net::__anonbb611ec80211::OrderByCreationTimeDesc248   bool operator()(const CookieMonster::CookieMap::iterator& a,
249                   const CookieMonster::CookieMap::iterator& b) const {
250     return a->second->CreationDate() > b->second->CreationDate();
251   }
252 };
253 
LRACookieSorter(const CookieMonster::CookieMap::iterator & it1,const CookieMonster::CookieMap::iterator & it2)254 bool LRACookieSorter(const CookieMonster::CookieMap::iterator& it1,
255                      const CookieMonster::CookieMap::iterator& it2) {
256   if (it1->second->LastAccessDate() != it2->second->LastAccessDate())
257     return it1->second->LastAccessDate() < it2->second->LastAccessDate();
258 
259   // Ensure stability for == last access times by falling back to creation.
260   return it1->second->CreationDate() < it2->second->CreationDate();
261 }
262 
263 // For a CookieItVector iterator range [|it_begin|, |it_end|),
264 // sorts the first |num_sort| elements by LastAccessDate().
SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,CookieMonster::CookieItVector::iterator it_end,size_t num_sort)265 void SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,
266                                CookieMonster::CookieItVector::iterator it_end,
267                                size_t num_sort) {
268   DCHECK_LE(static_cast<int>(num_sort), it_end - it_begin);
269   std::partial_sort(it_begin, it_begin + num_sort, it_end, LRACookieSorter);
270 }
271 
272 // Given a single cookie vector |cookie_its|, pushs all of the secure cookies in
273 // |cookie_its| into |secure_cookie_its| and all of the non-secure cookies into
274 // |non_secure_cookie_its|. Both |secure_cookie_its| and |non_secure_cookie_its|
275 // must be non-NULL.
SplitCookieVectorIntoSecureAndNonSecure(const CookieMonster::CookieItVector & cookie_its,CookieMonster::CookieItVector * secure_cookie_its,CookieMonster::CookieItVector * non_secure_cookie_its)276 void SplitCookieVectorIntoSecureAndNonSecure(
277     const CookieMonster::CookieItVector& cookie_its,
278     CookieMonster::CookieItVector* secure_cookie_its,
279     CookieMonster::CookieItVector* non_secure_cookie_its) {
280   DCHECK(secure_cookie_its && non_secure_cookie_its);
281   for (const auto& curit : cookie_its) {
282     if (curit->second->SecureAttribute()) {
283       secure_cookie_its->push_back(curit);
284     } else {
285       non_secure_cookie_its->push_back(curit);
286     }
287   }
288 }
289 
LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,const Time & access_date)290 bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,
291                                     const Time& access_date) {
292   return it->second->LastAccessDate() < access_date;
293 }
294 
295 // For a CookieItVector iterator range [|it_begin|, |it_end|)
296 // from a CookieItVector sorted by LastAccessDate(), returns the
297 // first iterator with access date >= |access_date|, or cookie_its_end if this
298 // holds for all.
LowerBoundAccessDate(const CookieMonster::CookieItVector::iterator its_begin,const CookieMonster::CookieItVector::iterator its_end,const Time & access_date)299 CookieMonster::CookieItVector::iterator LowerBoundAccessDate(
300     const CookieMonster::CookieItVector::iterator its_begin,
301     const CookieMonster::CookieItVector::iterator its_end,
302     const Time& access_date) {
303   return std::lower_bound(its_begin, its_end, access_date,
304                           LowerBoundAccessDateComparator);
305 }
306 
307 // Mapping between DeletionCause and CookieChangeCause; the
308 // mapping also provides a boolean that specifies whether or not an
309 // OnCookieChange notification ought to be generated.
310 typedef struct ChangeCausePair_struct {
311   CookieChangeCause cause;
312   bool notify;
313 } ChangeCausePair;
314 const ChangeCausePair kChangeCauseMapping[] = {
315     // DELETE_COOKIE_EXPLICIT
316     {CookieChangeCause::EXPLICIT, true},
317     // DELETE_COOKIE_OVERWRITE
318     {CookieChangeCause::OVERWRITE, true},
319     // DELETE_COOKIE_EXPIRED
320     {CookieChangeCause::EXPIRED, true},
321     // DELETE_COOKIE_EVICTED
322     {CookieChangeCause::EVICTED, true},
323     // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
324     {CookieChangeCause::EXPLICIT, false},
325     // DELETE_COOKIE_DONT_RECORD
326     {CookieChangeCause::EXPLICIT, false},
327     // DELETE_COOKIE_EVICTED_DOMAIN
328     {CookieChangeCause::EVICTED, true},
329     // DELETE_COOKIE_EVICTED_GLOBAL
330     {CookieChangeCause::EVICTED, true},
331     // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
332     {CookieChangeCause::EVICTED, true},
333     // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
334     {CookieChangeCause::EVICTED, true},
335     // DELETE_COOKIE_EXPIRED_OVERWRITE
336     {CookieChangeCause::EXPIRED_OVERWRITE, true},
337     // DELETE_COOKIE_CONTROL_CHAR
338     {CookieChangeCause::EVICTED, true},
339     // DELETE_COOKIE_NON_SECURE
340     {CookieChangeCause::EVICTED, true},
341     // DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN
342     {CookieChangeCause::EVICTED, true},
343     // DELETE_COOKIE_LAST_ENTRY
344     {CookieChangeCause::EXPLICIT, false}};
345 
IsCookieEligibleForEviction(CookiePriority current_priority_level,bool protect_secure_cookies,const CanonicalCookie * cookie)346 bool IsCookieEligibleForEviction(CookiePriority current_priority_level,
347                                  bool protect_secure_cookies,
348                                  const CanonicalCookie* cookie) {
349   if (cookie->Priority() == current_priority_level && protect_secure_cookies)
350     return !cookie->SecureAttribute();
351 
352   return cookie->Priority() == current_priority_level;
353 }
354 
CountCookiesForPossibleDeletion(CookiePriority priority,const CookieMonster::CookieItVector * cookies,bool protect_secure_cookies)355 size_t CountCookiesForPossibleDeletion(
356     CookiePriority priority,
357     const CookieMonster::CookieItVector* cookies,
358     bool protect_secure_cookies) {
359   size_t cookies_count = 0U;
360   for (const auto& cookie : *cookies) {
361     if (cookie->second->Priority() == priority) {
362       if (!protect_secure_cookies || cookie->second->SecureAttribute()) {
363         cookies_count++;
364       }
365     }
366   }
367   return cookies_count;
368 }
369 
370 struct DeletionCookieLists {
371   std::list<CookieMonster::CookieItList::const_iterator> host_cookies;
372   std::list<CookieMonster::CookieItList::const_iterator> domain_cookies;
373 };
374 
375 // Performs 2 tasks
376 // * Counts every cookie at the given `priority` in `cookies`. This is the
377 // return value.
378 // * Fills in the host & domain lists for `could_be_deleted` with every cookie
379 // of the given {secureness, priority} in `cookies`.
CountCookiesAndGenerateListsForPossibleDeletion(CookiePriority priority,DeletionCookieLists & could_be_deleted,const CookieMonster::CookieItList * cookies,bool generate_for_secure)380 size_t CountCookiesAndGenerateListsForPossibleDeletion(
381     CookiePriority priority,
382     DeletionCookieLists& could_be_deleted,
383     const CookieMonster::CookieItList* cookies,
384     bool generate_for_secure) {
385   size_t total_cookies_at_priority = 0;
386 
387   for (auto list_it = cookies->begin(); list_it != cookies->end(); list_it++) {
388     const auto cookiemap_it = *list_it;
389     const auto& cookie = cookiemap_it->second;
390 
391     if (cookie->Priority() != priority) {
392       continue;
393     }
394 
395     // Because we want to keep a specific number of cookies per priority level,
396     // independent of securness of the cookies, we need to count all the cookies
397     // at the level even if we'll skip adding them to the deletion lists.
398     total_cookies_at_priority++;
399 
400     if (cookie->IsSecure() != generate_for_secure) {
401       continue;
402     }
403 
404     if (cookie->IsHostCookie()) {
405       could_be_deleted.host_cookies.push_back(list_it);
406     } else {  // Is a domain cookie.
407       could_be_deleted.domain_cookies.push_back(list_it);
408     }
409   }
410 
411   return total_cookies_at_priority;
412 }
413 
414 // Records minutes until the expiration date of a cookie to the appropriate
415 // histogram. Only histograms cookies that have an expiration date (i.e. are
416 // persistent).
HistogramExpirationDuration(const CanonicalCookie & cookie,base::Time creation_time)417 void HistogramExpirationDuration(const CanonicalCookie& cookie,
418                                  base::Time creation_time) {
419   if (!cookie.IsPersistent())
420     return;
421 
422   int expiration_duration_minutes =
423       (cookie.ExpiryDate() - creation_time).InMinutes();
424   if (cookie.SecureAttribute()) {
425     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDurationMinutesSecure",
426                                 expiration_duration_minutes, 1,
427                                 kMinutesInTenYears, 50);
428   } else {
429     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDurationMinutesNonSecure",
430                                 expiration_duration_minutes, 1,
431                                 kMinutesInTenYears, 50);
432   }
433   // The proposed rfc6265bis sets an upper limit on Expires/Max-Age attribute
434   // values of 400 days. We need to study the impact this change would have:
435   // https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html
436   int expiration_duration_days = (cookie.ExpiryDate() - creation_time).InDays();
437   if (expiration_duration_days > 400) {
438     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDuration400DaysGT",
439                                 expiration_duration_days, 401, kDaysInTenYears,
440                                 100);
441   } else {
442     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDuration400DaysLTE",
443                                 expiration_duration_days, 1, 400, 50);
444   }
445 }
446 
447 }  // namespace
448 
CookieMonster(scoped_refptr<PersistentCookieStore> store,NetLog * net_log)449 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
450                              NetLog* net_log)
451     : CookieMonster(std::move(store),
452                     base::Seconds(kDefaultAccessUpdateThresholdSeconds),
453                     net_log) {}
454 
CookieMonster(scoped_refptr<PersistentCookieStore> store,base::TimeDelta last_access_threshold,NetLog * net_log)455 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
456                              base::TimeDelta last_access_threshold,
457                              NetLog* net_log)
458     : change_dispatcher_(this),
459       net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::COOKIE_STORE)),
460       store_(std::move(store)),
461       last_access_threshold_(last_access_threshold),
462       last_statistic_record_time_(base::Time::Now()) {
463   cookieable_schemes_.insert(
464       cookieable_schemes_.begin(), kDefaultCookieableSchemes,
465       kDefaultCookieableSchemes + kDefaultCookieableSchemesCount);
466   net_log_.BeginEvent(NetLogEventType::COOKIE_STORE_ALIVE, [&] {
467     return NetLogCookieMonsterConstructorParams(store_ != nullptr);
468   });
469 }
470 
471 // Asynchronous CookieMonster API
472 
FlushStore(base::OnceClosure callback)473 void CookieMonster::FlushStore(base::OnceClosure callback) {
474   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
475 
476   if (initialized_ && store_.get()) {
477     store_->Flush(std::move(callback));
478   } else if (callback) {
479     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
480         FROM_HERE, std::move(callback));
481   }
482 }
483 
SetForceKeepSessionState()484 void CookieMonster::SetForceKeepSessionState() {
485   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
486 
487   if (store_)
488     store_->SetForceKeepSessionState();
489 }
490 
SetAllCookiesAsync(const CookieList & list,SetCookiesCallback callback)491 void CookieMonster::SetAllCookiesAsync(const CookieList& list,
492                                        SetCookiesCallback callback) {
493   DoCookieCallback(base::BindOnce(
494       // base::Unretained is safe as DoCookieCallback stores
495       // the callback on |*this|, so the callback will not outlive
496       // the object.
497       &CookieMonster::SetAllCookies, base::Unretained(this), list,
498       std::move(callback)));
499 }
500 
SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,const GURL & source_url,const CookieOptions & options,SetCookiesCallback callback,std::optional<CookieAccessResult> cookie_access_result)501 void CookieMonster::SetCanonicalCookieAsync(
502     std::unique_ptr<CanonicalCookie> cookie,
503     const GURL& source_url,
504     const CookieOptions& options,
505     SetCookiesCallback callback,
506     std::optional<CookieAccessResult> cookie_access_result) {
507   DCHECK(cookie->IsCanonical());
508 
509   std::string domain = cookie->Domain();
510   DoCookieCallbackForHostOrDomain(
511       base::BindOnce(
512           // base::Unretained is safe as DoCookieCallbackForHostOrDomain stores
513           // the callback on |*this|, so the callback will not outlive
514           // the object.
515           &CookieMonster::SetCanonicalCookie, base::Unretained(this),
516           std::move(cookie), source_url, options, std::move(callback),
517           std::move(cookie_access_result)),
518       domain);
519 }
520 
GetCookieListWithOptionsAsync(const GURL & url,const CookieOptions & options,const CookiePartitionKeyCollection & cookie_partition_key_collection,GetCookieListCallback callback)521 void CookieMonster::GetCookieListWithOptionsAsync(
522     const GURL& url,
523     const CookieOptions& options,
524     const CookiePartitionKeyCollection& cookie_partition_key_collection,
525     GetCookieListCallback callback) {
526   DoCookieCallbackForURL(
527       base::BindOnce(
528           // base::Unretained is safe as DoCookieCallbackForURL stores
529           // the callback on |*this|, so the callback will not outlive
530           // the object.
531           &CookieMonster::GetCookieListWithOptions, base::Unretained(this), url,
532           options, cookie_partition_key_collection, std::move(callback)),
533       url);
534 }
535 
GetAllCookiesAsync(GetAllCookiesCallback callback)536 void CookieMonster::GetAllCookiesAsync(GetAllCookiesCallback callback) {
537   DoCookieCallback(base::BindOnce(
538       // base::Unretained is safe as DoCookieCallback stores
539       // the callback on |*this|, so the callback will not outlive
540       // the object.
541       &CookieMonster::GetAllCookies, base::Unretained(this),
542       std::move(callback)));
543 }
544 
GetAllCookiesWithAccessSemanticsAsync(GetAllCookiesWithAccessSemanticsCallback callback)545 void CookieMonster::GetAllCookiesWithAccessSemanticsAsync(
546     GetAllCookiesWithAccessSemanticsCallback callback) {
547   DoCookieCallback(base::BindOnce(
548       // base::Unretained is safe as DoCookieCallback stores
549       // the callback on |*this|, so the callback will not outlive
550       // the object.
551       &CookieMonster::GetAllCookies, base::Unretained(this),
552       base::BindOnce(&CookieMonster::AttachAccessSemanticsListForCookieList,
553                      base::Unretained(this), std::move(callback))));
554 }
555 
DeleteCanonicalCookieAsync(const CanonicalCookie & cookie,DeleteCallback callback)556 void CookieMonster::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
557                                                DeleteCallback callback) {
558   DoCookieCallback(base::BindOnce(
559       // base::Unretained is safe as DoCookieCallback stores
560       // the callback on |*this|, so the callback will not outlive
561       // the object.
562       &CookieMonster::DeleteCanonicalCookie, base::Unretained(this), cookie,
563       std::move(callback)));
564 }
565 
DeleteAllCreatedInTimeRangeAsync(const TimeRange & creation_range,DeleteCallback callback)566 void CookieMonster::DeleteAllCreatedInTimeRangeAsync(
567     const TimeRange& creation_range,
568     DeleteCallback callback) {
569   DoCookieCallback(base::BindOnce(
570       // base::Unretained is safe as DoCookieCallback stores
571       // the callback on |*this|, so the callback will not outlive
572       // the object.
573       &CookieMonster::DeleteAllCreatedInTimeRange, base::Unretained(this),
574       creation_range, std::move(callback)));
575 }
576 
DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,DeleteCallback callback)577 void CookieMonster::DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
578                                                DeleteCallback callback) {
579   auto cookie_matcher =
580       base::BindRepeating(&CookieMonster::MatchCookieDeletionInfo,
581                           base::Unretained(this), std::move(delete_info));
582 
583   DoCookieCallback(base::BindOnce(
584       // base::Unretained is safe as DoCookieCallback stores
585       // the callback on |*this|, so the callback will not outlive
586       // the object.
587       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
588       std::move(cookie_matcher), DELETE_COOKIE_EXPLICIT, std::move(callback)));
589 }
590 
DeleteSessionCookiesAsync(CookieStore::DeleteCallback callback)591 void CookieMonster::DeleteSessionCookiesAsync(
592     CookieStore::DeleteCallback callback) {
593   auto session_cookie_matcher =
594       base::BindRepeating([](const net::CanonicalCookie& cookie) {
595         return !cookie.IsPersistent();
596       });
597   DoCookieCallback(base::BindOnce(
598       // base::Unretained is safe as DoCookieCallback stores
599       // the callback on |*this|, so the callback will not outlive
600       // the object.
601       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
602       std::move(session_cookie_matcher), DELETE_COOKIE_EXPIRED,
603       std::move(callback)));
604 }
605 
DeleteMatchingCookiesAsync(CookieStore::DeletePredicate predicate,CookieStore::DeleteCallback callback)606 void CookieMonster::DeleteMatchingCookiesAsync(
607     CookieStore::DeletePredicate predicate,
608     CookieStore::DeleteCallback callback) {
609   DoCookieCallback(base::BindOnce(
610       // base::Unretained is safe as DoCookieCallback stores
611       // the callback on |*this|, so the callback will not outlive
612       // the object.
613       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
614       std::move(predicate), DELETE_COOKIE_EXPLICIT, std::move(callback)));
615 }
616 
SetCookieableSchemes(const std::vector<std::string> & schemes,SetCookieableSchemesCallback callback)617 void CookieMonster::SetCookieableSchemes(
618     const std::vector<std::string>& schemes,
619     SetCookieableSchemesCallback callback) {
620   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
621 
622   // Calls to this method will have no effect if made after a WebView or
623   // CookieManager instance has been created.
624   if (initialized_) {
625     MaybeRunCookieCallback(std::move(callback), false);
626     return;
627   }
628 
629   cookieable_schemes_ = schemes;
630   MaybeRunCookieCallback(std::move(callback), true);
631 }
632 
633 // This function must be called before the CookieMonster is used.
SetPersistSessionCookies(bool persist_session_cookies)634 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
635   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
636   DCHECK(!initialized_);
637   net_log_.AddEntryWithBoolParams(
638       NetLogEventType::COOKIE_STORE_SESSION_PERSISTENCE, NetLogEventPhase::NONE,
639       "persistence", persist_session_cookies);
640   persist_session_cookies_ = persist_session_cookies;
641 }
642 
643 const char* const CookieMonster::kDefaultCookieableSchemes[] = {"http", "https",
644                                                                 "ws", "wss"};
645 const int CookieMonster::kDefaultCookieableSchemesCount =
646     std::size(kDefaultCookieableSchemes);
647 
GetChangeDispatcher()648 CookieChangeDispatcher& CookieMonster::GetChangeDispatcher() {
649   return change_dispatcher_;
650 }
651 
~CookieMonster()652 CookieMonster::~CookieMonster() {
653   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
654   net_log_.EndEvent(NetLogEventType::COOKIE_STORE_ALIVE);
655 }
656 
657 // static
CookieSorter(const CanonicalCookie * cc1,const CanonicalCookie * cc2)658 bool CookieMonster::CookieSorter(const CanonicalCookie* cc1,
659                                  const CanonicalCookie* cc2) {
660   // Mozilla sorts on the path length (longest first), and then it sorts by
661   // creation time (oldest first).  The RFC says the sort order for the domain
662   // attribute is undefined.
663   if (cc1->Path().length() == cc2->Path().length())
664     return cc1->CreationDate() < cc2->CreationDate();
665   return cc1->Path().length() > cc2->Path().length();
666 }
667 
GetAllCookies(GetAllCookiesCallback callback)668 void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {
669   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
670 
671   // This function is being called to scrape the cookie list for management UI
672   // or similar.  We shouldn't show expired cookies in this list since it will
673   // just be confusing to users, and this function is called rarely enough (and
674   // is already slow enough) that it's OK to take the time to garbage collect
675   // the expired cookies now.
676   //
677   // Note that this does not prune cookies to be below our limits (if we've
678   // exceeded them) the way that calling GarbageCollect() would.
679   GarbageCollectExpired(
680       Time::Now(), CookieMapItPair(cookies_.begin(), cookies_.end()), nullptr);
681   GarbageCollectAllExpiredPartitionedCookies(Time::Now());
682 
683   // Copy the CanonicalCookie pointers from the map so that we can use the same
684   // sorter as elsewhere, then copy the result out.
685   std::vector<CanonicalCookie*> cookie_ptrs;
686   cookie_ptrs.reserve(cookies_.size());
687   for (const auto& cookie : cookies_)
688     cookie_ptrs.push_back(cookie.second.get());
689 
690   for (const auto& cookie_partition : partitioned_cookies_) {
691     for (const auto& cookie : *cookie_partition.second.get())
692       cookie_ptrs.push_back(cookie.second.get());
693   }
694 
695   std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
696 
697   CookieList cookie_list;
698   cookie_list.reserve(cookie_ptrs.size());
699   for (auto* cookie_ptr : cookie_ptrs)
700     cookie_list.push_back(*cookie_ptr);
701 
702   MaybeRunCookieCallback(std::move(callback), cookie_list);
703 }
704 
AttachAccessSemanticsListForCookieList(GetAllCookiesWithAccessSemanticsCallback callback,const CookieList & cookie_list)705 void CookieMonster::AttachAccessSemanticsListForCookieList(
706     GetAllCookiesWithAccessSemanticsCallback callback,
707     const CookieList& cookie_list) {
708   std::vector<CookieAccessSemantics> access_semantics_list;
709   for (const CanonicalCookie& cookie : cookie_list) {
710     access_semantics_list.push_back(GetAccessSemanticsForCookie(cookie));
711   }
712   MaybeRunCookieCallback(std::move(callback), cookie_list,
713                          access_semantics_list);
714 }
715 
GetCookieListWithOptions(const GURL & url,const CookieOptions & options,const CookiePartitionKeyCollection & cookie_partition_key_collection,GetCookieListCallback callback)716 void CookieMonster::GetCookieListWithOptions(
717     const GURL& url,
718     const CookieOptions& options,
719     const CookiePartitionKeyCollection& cookie_partition_key_collection,
720     GetCookieListCallback callback) {
721   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
722 
723   CookieAccessResultList included_cookies;
724   CookieAccessResultList excluded_cookies;
725   if (HasCookieableScheme(url)) {
726     std::vector<CanonicalCookie*> cookie_ptrs;
727     if (IncludeUnpartitionedCookies(cookie_partition_key_collection)) {
728       cookie_ptrs = FindCookiesForRegistryControlledHost(url);
729     } else {
730       DCHECK(!cookie_partition_key_collection.IsEmpty());
731     }
732 
733     if (!cookie_partition_key_collection.IsEmpty()) {
734       if (cookie_partition_key_collection.ContainsAllKeys()) {
735         for (const auto& it : partitioned_cookies_) {
736           std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
737               FindPartitionedCookiesForRegistryControlledHost(it.first, url);
738           cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
739                              partitioned_cookie_ptrs.end());
740         }
741       } else {
742         for (const CookiePartitionKey& key :
743              cookie_partition_key_collection.PartitionKeys()) {
744           std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
745               FindPartitionedCookiesForRegistryControlledHost(key, url);
746           cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
747                              partitioned_cookie_ptrs.end());
748         }
749       }
750     }
751     std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
752 
753     included_cookies.reserve(cookie_ptrs.size());
754     FilterCookiesWithOptions(url, options, &cookie_ptrs, &included_cookies,
755                              &excluded_cookies);
756   }
757 
758   MaybeRunCookieCallback(std::move(callback), included_cookies,
759                          excluded_cookies);
760 }
761 
DeleteAllCreatedInTimeRange(const TimeRange & creation_range,DeleteCallback callback)762 void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
763                                                 DeleteCallback callback) {
764   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
765 
766   uint32_t num_deleted = 0;
767   for (auto it = cookies_.begin(); it != cookies_.end();) {
768     auto curit = it;
769     CanonicalCookie* cc = curit->second.get();
770     ++it;
771 
772     if (creation_range.Contains(cc->CreationDate())) {
773       InternalDeleteCookie(curit, true, /*sync_to_store*/
774                            DELETE_COOKIE_EXPLICIT);
775       ++num_deleted;
776     }
777   }
778 
779   for (PartitionedCookieMap::iterator partition_it =
780            partitioned_cookies_.begin();
781        partition_it != partitioned_cookies_.end();) {
782     auto cur_partition_it = partition_it;
783     CookieMap::iterator cookie_it = cur_partition_it->second->begin();
784     CookieMap::iterator cookie_end = cur_partition_it->second->end();
785     // InternalDeletePartitionedCookie may delete this cookie partition if it
786     // only has one cookie, so we need to increment the iterator beforehand.
787     ++partition_it;
788 
789     while (cookie_it != cookie_end) {
790       auto cur_cookie_it = cookie_it;
791       CanonicalCookie* cc = cur_cookie_it->second.get();
792       ++cookie_it;
793 
794       if (creation_range.Contains(cc->CreationDate())) {
795         InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it,
796                                         true /*sync_to_store*/,
797                                         DELETE_COOKIE_EXPLICIT);
798         ++num_deleted;
799       }
800     }
801   }
802 
803   FlushStore(
804       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
805                      callback ? base::BindOnce(std::move(callback), num_deleted)
806                               : base::OnceClosure()));
807 }
808 
MatchCookieDeletionInfo(const CookieDeletionInfo & delete_info,const net::CanonicalCookie & cookie)809 bool CookieMonster::MatchCookieDeletionInfo(
810     const CookieDeletionInfo& delete_info,
811     const net::CanonicalCookie& cookie) {
812   bool delegate_treats_url_as_trustworthy = false;  // irrelevant if no URL.
813   if (delete_info.url.has_value()) {
814     delegate_treats_url_as_trustworthy =
815         cookie_access_delegate() &&
816         cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(
817             delete_info.url.value());
818   }
819 
820   return delete_info.Matches(
821       cookie, CookieAccessParams{GetAccessSemanticsForCookie(cookie),
822                                  delegate_treats_url_as_trustworthy});
823 }
824 
DeleteCanonicalCookie(const CanonicalCookie & cookie,DeleteCallback callback)825 void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
826                                           DeleteCallback callback) {
827   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
828   uint32_t result = 0u;
829   CookieMap* cookie_map = nullptr;
830   PartitionedCookieMap::iterator cookie_partition_it;
831 
832   if (cookie.IsPartitioned()) {
833     cookie_partition_it =
834         partitioned_cookies_.find(cookie.PartitionKey().value());
835     if (cookie_partition_it != partitioned_cookies_.end())
836       cookie_map = cookie_partition_it->second.get();
837   } else {
838     cookie_map = &cookies_;
839   }
840   if (cookie_map) {
841     for (CookieMapItPair its = cookie_map->equal_range(GetKey(cookie.Domain()));
842          its.first != its.second; ++its.first) {
843       const std::unique_ptr<CanonicalCookie>& candidate = its.first->second;
844       // Historically, this has refused modification if the cookie has changed
845       // value in between the CanonicalCookie object was returned by a getter
846       // and when this ran.  The later parts of the conditional (everything but
847       // the equivalence check) attempt to preserve this behavior.
848       if (candidate->IsEquivalent(cookie) &&
849           candidate->Value() == cookie.Value()) {
850         if (cookie.IsPartitioned()) {
851           InternalDeletePartitionedCookie(cookie_partition_it, its.first, true,
852                                           DELETE_COOKIE_EXPLICIT);
853         } else {
854           InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
855         }
856         result = 1u;
857         break;
858       }
859     }
860   }
861   FlushStore(
862       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
863                      callback ? base::BindOnce(std::move(callback), result)
864                               : base::OnceClosure()));
865 }
866 
DeleteMatchingCookies(DeletePredicate predicate,DeletionCause cause,DeleteCallback callback)867 void CookieMonster::DeleteMatchingCookies(DeletePredicate predicate,
868                                           DeletionCause cause,
869                                           DeleteCallback callback) {
870   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
871   DCHECK(predicate);
872 
873   uint32_t num_deleted = 0;
874   for (auto it = cookies_.begin(); it != cookies_.end();) {
875     auto curit = it;
876     CanonicalCookie* cc = curit->second.get();
877     ++it;
878     if (predicate.Run(*cc)) {
879       InternalDeleteCookie(curit, true /*sync_to_store*/, cause);
880       ++num_deleted;
881     }
882   }
883   for (auto partition_it = partitioned_cookies_.begin();
884        partition_it != partitioned_cookies_.end();) {
885     // InternalDeletePartitionedCookie may invalidate |partition_it| if that
886     // cookie partition only has one cookie.
887     auto cur_partition_it = partition_it;
888     CookieMap::iterator cookie_it = cur_partition_it->second->begin();
889     CookieMap::iterator cookie_end = cur_partition_it->second->end();
890     ++partition_it;
891 
892     while (cookie_it != cookie_end) {
893       auto cur_cookie_it = cookie_it;
894       CanonicalCookie* cc = cur_cookie_it->second.get();
895       ++cookie_it;
896 
897       if (predicate.Run(*cc)) {
898         InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it, true,
899                                         cause);
900         ++num_deleted;
901       }
902     }
903   }
904 
905   FlushStore(
906       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
907                      callback ? base::BindOnce(std::move(callback), num_deleted)
908                               : base::OnceClosure()));
909 }
910 
MarkCookieStoreAsInitialized()911 void CookieMonster::MarkCookieStoreAsInitialized() {
912   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
913   initialized_ = true;
914 }
915 
FetchAllCookiesIfNecessary()916 void CookieMonster::FetchAllCookiesIfNecessary() {
917   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
918   if (store_.get() && !started_fetching_all_cookies_) {
919     started_fetching_all_cookies_ = true;
920     FetchAllCookies();
921   }
922 }
923 
FetchAllCookies()924 void CookieMonster::FetchAllCookies() {
925   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
926   DCHECK(store_.get()) << "Store must exist to initialize";
927   DCHECK(!finished_fetching_all_cookies_)
928       << "All cookies have already been fetched.";
929 
930   // We bind in the current time so that we can report the wall-clock time for
931   // loading cookies.
932   store_->Load(base::BindOnce(&CookieMonster::OnLoaded,
933                               weak_ptr_factory_.GetWeakPtr(), TimeTicks::Now()),
934                net_log_);
935 }
936 
OnLoaded(TimeTicks beginning_time,std::vector<std::unique_ptr<CanonicalCookie>> cookies)937 void CookieMonster::OnLoaded(
938     TimeTicks beginning_time,
939     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
940   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
941   StoreLoadedCookies(std::move(cookies));
942   base::UmaHistogramCustomTimes("Cookie.TimeBlockedOnLoad",
943                                 base::TimeTicks::Now() - beginning_time,
944                                 base::Milliseconds(1), base::Minutes(1), 50);
945 
946   // Invoke the task queue of cookie request.
947   InvokeQueue();
948 }
949 
OnKeyLoaded(const std::string & key,std::vector<std::unique_ptr<CanonicalCookie>> cookies)950 void CookieMonster::OnKeyLoaded(
951     const std::string& key,
952     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
953   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
954 
955   StoreLoadedCookies(std::move(cookies));
956 
957   auto tasks_pending_for_key = tasks_pending_for_key_.find(key);
958 
959   // TODO(mmenke): Can this be turned into a DCHECK?
960   if (tasks_pending_for_key == tasks_pending_for_key_.end())
961     return;
962 
963   // Run all tasks for the key. Note that running a task can result in multiple
964   // tasks being added to the back of the deque.
965   while (!tasks_pending_for_key->second.empty()) {
966     base::OnceClosure task = std::move(tasks_pending_for_key->second.front());
967     tasks_pending_for_key->second.pop_front();
968     std::move(task).Run();
969   }
970 
971   tasks_pending_for_key_.erase(tasks_pending_for_key);
972 
973   // This has to be done last, in case running a task queues a new task for the
974   // key, to ensure tasks are run in the correct order.
975   keys_loaded_.insert(key);
976 }
977 
StoreLoadedCookies(std::vector<std::unique_ptr<CanonicalCookie>> cookies)978 void CookieMonster::StoreLoadedCookies(
979     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
980   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
981 
982   // Even if a key is expired, insert it so it can be garbage collected,
983   // removed, and sync'd.
984   CookieItVector cookies_with_control_chars;
985   std::vector<PartitionedCookieMapIterators>
986       partitioned_cookies_with_control_chars;
987 
988   for (auto& cookie : cookies) {
989     CanonicalCookie* cookie_ptr = cookie.get();
990     CookieAccessResult access_result;
991     access_result.access_semantics = CookieAccessSemantics::UNKNOWN;
992 
993     if (cookie_ptr->IsPartitioned()) {
994       auto inserted = InternalInsertPartitionedCookie(
995           GetKey(cookie_ptr->Domain()), std::move(cookie),
996           false /* sync_to_store */, access_result,
997           false /* dispatch_change */);
998       if (ContainsControlCharacter(cookie_ptr->Name()) ||
999           ContainsControlCharacter(cookie_ptr->Value())) {
1000         partitioned_cookies_with_control_chars.push_back(inserted);
1001       }
1002     } else {
1003       auto inserted =
1004           InternalInsertCookie(GetKey(cookie_ptr->Domain()), std::move(cookie),
1005                                false /* sync_to_store */, access_result,
1006                                false /* dispatch_change */);
1007 
1008       if (ContainsControlCharacter(cookie_ptr->Name()) ||
1009           ContainsControlCharacter(cookie_ptr->Value())) {
1010         cookies_with_control_chars.push_back(inserted);
1011       }
1012     }
1013 
1014     const Time cookie_access_time(cookie_ptr->LastAccessDate());
1015     if (earliest_access_time_.is_null() ||
1016         cookie_access_time < earliest_access_time_) {
1017       earliest_access_time_ = cookie_access_time;
1018     }
1019   }
1020 
1021   // Any cookies that contain control characters that we have loaded from the
1022   // persistent store should be deleted. See http://crbug.com/238041.
1023   for (auto it = cookies_with_control_chars.begin();
1024        it != cookies_with_control_chars.end();) {
1025     auto curit = it;
1026     ++it;
1027     InternalDeleteCookie(*curit, true, DELETE_COOKIE_CONTROL_CHAR);
1028   }
1029   for (auto it = partitioned_cookies_with_control_chars.begin();
1030        it != partitioned_cookies_with_control_chars.end();) {
1031     // InternalDeletePartitionedCookie may invalidate the current iterator, so
1032     // we increment the iterator in the loop before calling the function.
1033     auto curit = it;
1034     ++it;
1035     InternalDeletePartitionedCookie(curit->first, curit->second, true,
1036                                     DELETE_COOKIE_CONTROL_CHAR);
1037   }
1038 
1039   // After importing cookies from the PersistentCookieStore, verify that
1040   // none of our other constraints are violated.
1041   // In particular, the backing store might have given us duplicate cookies.
1042 
1043   // This method could be called multiple times due to priority loading, thus
1044   // cookies loaded in previous runs will be validated again, but this is OK
1045   // since they are expected to be much fewer than total DB.
1046   EnsureCookiesMapIsValid();
1047 }
1048 
InvokeQueue()1049 void CookieMonster::InvokeQueue() {
1050   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1051 
1052   // Move all per-key tasks into the global queue, if there are any.  This is
1053   // protection about a race where the store learns about all cookies loading
1054   // before it learned about the cookies for a key loading.
1055 
1056   // Needed to prevent any recursively queued tasks from going back into the
1057   // per-key queues.
1058   seen_global_task_ = true;
1059   for (auto& tasks_for_key : tasks_pending_for_key_) {
1060     tasks_pending_.insert(tasks_pending_.begin(),
1061                           std::make_move_iterator(tasks_for_key.second.begin()),
1062                           std::make_move_iterator(tasks_for_key.second.end()));
1063   }
1064   tasks_pending_for_key_.clear();
1065 
1066   while (!tasks_pending_.empty()) {
1067     base::OnceClosure request_task = std::move(tasks_pending_.front());
1068     tasks_pending_.pop_front();
1069     std::move(request_task).Run();
1070   }
1071 
1072   DCHECK(tasks_pending_for_key_.empty());
1073 
1074   finished_fetching_all_cookies_ = true;
1075   keys_loaded_.clear();
1076 }
1077 
EnsureCookiesMapIsValid()1078 void CookieMonster::EnsureCookiesMapIsValid() {
1079   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1080 
1081   // Iterate through all the of the cookies, grouped by host.
1082   for (auto next = cookies_.begin(); next != cookies_.end();) {
1083     auto cur_range_begin = next;
1084     const std::string key = cur_range_begin->first;  // Keep a copy.
1085     auto cur_range_end = cookies_.upper_bound(key);
1086     next = cur_range_end;
1087 
1088     // Ensure no equivalent cookies for this host.
1089     TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
1090                                std::nullopt);
1091   }
1092 
1093   for (auto cookie_partition_it = partitioned_cookies_.begin();
1094        cookie_partition_it != partitioned_cookies_.end();) {
1095     auto cur_cookie_partition_it = cookie_partition_it;
1096     ++cookie_partition_it;
1097 
1098     // Iterate through the cookies in this partition, grouped by host.
1099     CookieMap* cookie_partition = cur_cookie_partition_it->second.get();
1100     auto prev_range_end = cookie_partition->begin();
1101     while (prev_range_end != cookie_partition->end()) {
1102       auto cur_range_begin = prev_range_end;
1103       const std::string key = cur_range_begin->first;  // Keep a copy.
1104       auto cur_range_end = cookie_partition->upper_bound(key);
1105       prev_range_end = cur_range_end;
1106 
1107       // Ensure no equivalent cookies for this host and cookie partition key.
1108       TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
1109                                  std::make_optional(cur_cookie_partition_it));
1110     }
1111   }
1112 }
1113 
1114 // Our strategy to find duplicates is:
1115 // (1) Build a map from cookie unique key to
1116 //     {list of cookies with this signature, sorted by creation time}.
1117 // (2) For each list with more than 1 entry, keep the cookie having the
1118 //     most recent creation time, and delete the others.
1119 //
TrimDuplicateCookiesForKey(const std::string & key,CookieMap::iterator begin,CookieMap::iterator end,std::optional<PartitionedCookieMap::iterator> cookie_partition_it)1120 void CookieMonster::TrimDuplicateCookiesForKey(
1121     const std::string& key,
1122     CookieMap::iterator begin,
1123     CookieMap::iterator end,
1124     std::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
1125   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1126 
1127   // Set of cookies ordered by creation time.
1128   typedef std::multiset<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
1129 
1130   // Helper map we populate to find the duplicates.
1131   typedef std::map<CanonicalCookie::UniqueCookieKey, CookieSet> EquivalenceMap;
1132   typedef std::map<CanonicalCookie::UniqueDomainCookieKey, CookieSet>
1133       DomainEquivalenceMap;
1134   EquivalenceMap equivalent_cookies;
1135   DomainEquivalenceMap equivalent_domain_cookies;
1136 
1137   // The number of duplicate cookies that have been found.
1138   int num_duplicates = 0;
1139   int num_domain_duplicates = 0;
1140 
1141   // Iterate through all of the cookies in our range, and insert them into
1142   // the equivalence map.
1143   for (auto it = begin; it != end; ++it) {
1144     DCHECK_EQ(key, it->first);
1145     CanonicalCookie* cookie = it->second.get();
1146 
1147     if (cookie->IsHostCookie()) {
1148       CanonicalCookie::UniqueCookieKey signature(cookie->UniqueKey());
1149       CookieSet& set = equivalent_cookies[signature];
1150 
1151       // We found a duplicate!
1152       if (!set.empty()) {
1153         num_duplicates++;
1154       }
1155 
1156       // We save the iterator into |cookies_| rather than the actual cookie
1157       // pointer, since we may need to delete it later.
1158       set.insert(it);
1159     }
1160     // Is a domain cookie.
1161     else {
1162       CanonicalCookie::UniqueDomainCookieKey signature(
1163           cookie->UniqueDomainKey());
1164       CookieSet& domain_set = equivalent_domain_cookies[signature];
1165 
1166       // We found a duplicate!
1167       if (!domain_set.empty()) {
1168         num_domain_duplicates++;
1169       }
1170 
1171       // We save the iterator into |cookies_| rather than the actual cookie
1172       // pointer, since we may need to delete it later.
1173       domain_set.insert(it);
1174     }
1175   }
1176 
1177   // If there were no duplicates, we are done!
1178   if (num_duplicates == 0 && num_domain_duplicates == 0) {
1179     return;
1180   }
1181 
1182   // Make sure we find everything below that we did above.
1183   int num_duplicates_found = 0;
1184 
1185   // Otherwise, delete all the duplicate host cookies, both from our in-memory
1186   // store and from the backing store.
1187   for (std::pair<const CanonicalCookie::UniqueCookieKey, CookieSet>&
1188            equivalent_cookie : equivalent_cookies) {
1189     const CanonicalCookie::UniqueCookieKey& signature = equivalent_cookie.first;
1190     CookieSet& dupes = equivalent_cookie.second;
1191 
1192     if (dupes.size() <= 1) {
1193       continue;  // This cookiename/path has no duplicates.
1194     }
1195 
1196     num_duplicates_found += dupes.size() - 1;
1197 
1198     // Since |dupes| is sorted by creation time (descending), the first cookie
1199     // is the most recent one (or tied for it), so we will keep it. The rest are
1200     // duplicates.
1201     dupes.erase(dupes.begin());
1202 
1203     // TODO(crbug.com/1225444) Include cookie partition key in this log
1204     // statement as well if needed.
1205     // TODO(crbug.com/1170548): Include source scheme and source port.
1206     LOG(ERROR) << base::StringPrintf(
1207         "Found %d duplicate cookies for key='%s', "
1208         "with {name='%s', domain='%s', path='%s'}",
1209         static_cast<int>(dupes.size()), key.c_str(),
1210         std::get<1>(signature).c_str(), std::get<2>(signature).c_str(),
1211         std::get<3>(signature).c_str());
1212 
1213     // Remove all the cookies identified by |dupes|. It is valid to delete our
1214     // list of iterators one at a time, since |cookies_| is a multimap (they
1215     // don't invalidate existing iterators following deletion).
1216     for (const CookieMap::iterator& dupe : dupes) {
1217       if (cookie_partition_it) {
1218         InternalDeletePartitionedCookie(
1219             cookie_partition_it.value(), dupe, true,
1220             DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1221       } else {
1222         InternalDeleteCookie(dupe, true,
1223                              DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1224       }
1225     }
1226   }
1227   CHECK_EQ(num_duplicates, num_duplicates_found);
1228 
1229   // Do the same again for domain cookies.
1230 
1231   if (num_domain_duplicates == 0) {
1232     return;
1233   }
1234 
1235   int num_domain_duplicates_found = 0;
1236 
1237   for (std::pair<const CanonicalCookie::UniqueDomainCookieKey, CookieSet>&
1238            equivalent_domain_cookie : equivalent_domain_cookies) {
1239     const CanonicalCookie::UniqueDomainCookieKey& signature =
1240         equivalent_domain_cookie.first;
1241     CookieSet& dupes = equivalent_domain_cookie.second;
1242 
1243     if (dupes.size() <= 1) {
1244       continue;
1245     }
1246 
1247     num_domain_duplicates_found += dupes.size() - 1;
1248 
1249     // Since |dupes| is sorted by creation time (descending), the first cookie
1250     // is the most recent one (or tied for it), so we will keep it. The rest are
1251     // duplicates.
1252     dupes.erase(dupes.begin());
1253 
1254     // TODO(crbug.com/1225444) Include cookie partition key in this log
1255     // statement as well if needed.
1256     // TODO(crbug.com/1170548): Include source scheme and source port.
1257     LOG(ERROR) << base::StringPrintf(
1258         "Found %d duplicate domain cookies for key='%s', "
1259         "with {name='%s', domain='%s', path='%s'}",
1260         static_cast<int>(dupes.size()), key.c_str(),
1261         std::get<1>(signature).c_str(), std::get<2>(signature).c_str(),
1262         std::get<3>(signature).c_str());
1263 
1264     // Remove all the cookies identified by |dupes|. It is valid to delete our
1265     // list of iterators one at a time, since |cookies_| is a multimap (they
1266     // don't invalidate existing iterators following deletion).
1267     for (const CookieMap::iterator& dupe : dupes) {
1268       if (cookie_partition_it) {
1269         InternalDeletePartitionedCookie(
1270             cookie_partition_it.value(), dupe, true,
1271             DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1272       } else {
1273         InternalDeleteCookie(dupe, true,
1274                              DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1275       }
1276     }
1277   }
1278 
1279   CHECK_EQ(num_domain_duplicates, num_domain_duplicates_found);
1280 }
1281 
1282 std::vector<CanonicalCookie*>
FindCookiesForRegistryControlledHost(const GURL & url,CookieMap * cookie_map,CookieMonster::PartitionedCookieMap::iterator * partition_it)1283 CookieMonster::FindCookiesForRegistryControlledHost(
1284     const GURL& url,
1285     CookieMap* cookie_map,
1286     CookieMonster::PartitionedCookieMap::iterator* partition_it) {
1287   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1288 
1289   if (!cookie_map)
1290     cookie_map = &cookies_;
1291 
1292   Time current_time = Time::Now();
1293 
1294   // Retrieve all cookies for a given key
1295   const std::string key(GetKey(url.host_piece()));
1296 
1297   std::vector<CanonicalCookie*> cookies;
1298   for (CookieMapItPair its = cookie_map->equal_range(key);
1299        its.first != its.second;) {
1300     auto curit = its.first;
1301     CanonicalCookie* cc = curit->second.get();
1302     ++its.first;
1303 
1304     // If the cookie is expired, delete it.
1305     if (cc->IsExpired(current_time)) {
1306       if (cc->IsPartitioned()) {
1307         DCHECK(partition_it);
1308         DCHECK_EQ((*partition_it)->second.get(), cookie_map);
1309         InternalDeletePartitionedCookie(*partition_it, curit, true,
1310                                         DELETE_COOKIE_EXPIRED);
1311       } else {
1312         InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
1313       }
1314       continue;
1315     }
1316     cookies.push_back(cc);
1317   }
1318   return cookies;
1319 }
1320 
1321 std::vector<CanonicalCookie*>
FindPartitionedCookiesForRegistryControlledHost(const CookiePartitionKey & cookie_partition_key,const GURL & url)1322 CookieMonster::FindPartitionedCookiesForRegistryControlledHost(
1323     const CookiePartitionKey& cookie_partition_key,
1324     const GURL& url) {
1325   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1326 
1327   PartitionedCookieMap::iterator it =
1328       partitioned_cookies_.find(cookie_partition_key);
1329   if (it == partitioned_cookies_.end())
1330     return std::vector<CanonicalCookie*>();
1331 
1332   return FindCookiesForRegistryControlledHost(url, it->second.get(), &it);
1333 }
1334 
FilterCookiesWithOptions(const GURL url,const CookieOptions options,std::vector<CanonicalCookie * > * cookie_ptrs,CookieAccessResultList * included_cookies,CookieAccessResultList * excluded_cookies)1335 void CookieMonster::FilterCookiesWithOptions(
1336     const GURL url,
1337     const CookieOptions options,
1338     std::vector<CanonicalCookie*>* cookie_ptrs,
1339     CookieAccessResultList* included_cookies,
1340     CookieAccessResultList* excluded_cookies) {
1341   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1342 
1343   // Probe to save statistics relatively frequently.  We do it here rather
1344   // than in the set path as many websites won't set cookies, and we
1345   // want to collect statistics whenever the browser's being used.
1346   Time current_time = Time::Now();
1347   RecordPeriodicStats(current_time);
1348 
1349   bool delegate_treats_url_as_trustworthy =
1350       cookie_access_delegate() &&
1351       cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(url);
1352 
1353   std::vector<std::pair<CanonicalCookie*, CookieAccessResult>>
1354       cookies_and_access_results;
1355   cookies_and_access_results.reserve(cookie_ptrs->size());
1356   std::set<std::string> origin_cookie_names;
1357 
1358   for (CanonicalCookie* cookie_ptr : *cookie_ptrs) {
1359     // Filter out cookies that should not be included for a request to the
1360     // given |url|. HTTP only cookies are filtered depending on the passed
1361     // cookie |options|.
1362     CookieAccessResult access_result = cookie_ptr->IncludeForRequestURL(
1363         url, options,
1364         CookieAccessParams{GetAccessSemanticsForCookie(*cookie_ptr),
1365                            delegate_treats_url_as_trustworthy});
1366     cookies_and_access_results.emplace_back(cookie_ptr, access_result);
1367 
1368     // Record the names of all origin cookies that would be included if both
1369     // kEnablePortBoundCookies and kEnableSchemeBoundCookies are enabled.
1370     //
1371     // We DO want to record origin cookies that are being excluded for path
1372     // reasons, so we'll remove any potential path exclusions.
1373     CookieInclusionStatus status_copy = access_result.status;
1374     status_copy.RemoveExclusionReason(
1375         CookieInclusionStatus::EXCLUDE_NOT_ON_PATH);
1376 
1377     bool exclusion_or_warning =
1378         !status_copy.IsInclude() ||
1379         status_copy.HasWarningReason(
1380             CookieInclusionStatus::WARN_SCHEME_MISMATCH) ||
1381         status_copy.HasWarningReason(CookieInclusionStatus::WARN_PORT_MISMATCH);
1382 
1383     if (!exclusion_or_warning && cookie_ptr->IsHostCookie()) {
1384       origin_cookie_names.insert(cookie_ptr->Name());
1385     }
1386   }
1387 
1388   for (auto& cookie_result : cookies_and_access_results) {
1389     CanonicalCookie* cookie_ptr = cookie_result.first;
1390     CookieAccessResult& access_result = cookie_result.second;
1391 
1392     // We want to collect these metrics for cookies that would be included
1393     // without considering shadowing domain cookies.
1394     if (access_result.status.IsInclude()) {
1395       int destination_port = url.EffectiveIntPort();
1396 
1397       if (IsLocalhost(url)) {
1398         UMA_HISTOGRAM_ENUMERATION(
1399             "Cookie.Port.Read.Localhost",
1400             ReducePortRangeForCookieHistogram(destination_port));
1401         UMA_HISTOGRAM_ENUMERATION(
1402             "Cookie.Port.ReadDiffersFromSet.Localhost",
1403             IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1404                                             cookie_ptr->SourceScheme()));
1405       } else {
1406         UMA_HISTOGRAM_ENUMERATION(
1407             "Cookie.Port.Read.RemoteHost",
1408             ReducePortRangeForCookieHistogram(destination_port));
1409         UMA_HISTOGRAM_ENUMERATION(
1410             "Cookie.Port.ReadDiffersFromSet.RemoteHost",
1411             IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1412                                             cookie_ptr->SourceScheme()));
1413       }
1414 
1415       if (cookie_ptr->IsDomainCookie()) {
1416         UMA_HISTOGRAM_ENUMERATION(
1417             "Cookie.Port.ReadDiffersFromSet.DomainSet",
1418             IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1419                                             cookie_ptr->SourceScheme()));
1420       }
1421     }
1422 
1423     // Filter out any domain `cookie_ptr` which are shadowing origin cookies.
1424     // Don't apply domain shadowing exclusion/warning reason if `cookie_ptr` is
1425     // already being excluded/warned for scheme matching reasons (Note, domain
1426     // cookies match every port so they'll never get excluded/warned for port
1427     // reasons).
1428     bool scheme_mismatch =
1429         access_result.status.HasExclusionReason(
1430             CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH) ||
1431         access_result.status.HasWarningReason(
1432             CookieInclusionStatus::WARN_SCHEME_MISMATCH);
1433 
1434     if (cookie_ptr->IsDomainCookie() && !scheme_mismatch &&
1435         origin_cookie_names.count(cookie_ptr->Name())) {
1436       if (cookie_util::IsSchemeBoundCookiesEnabled()) {
1437         access_result.status.AddExclusionReason(
1438             CookieInclusionStatus::EXCLUDE_SHADOWING_DOMAIN);
1439       } else {
1440         access_result.status.AddWarningReason(
1441             CookieInclusionStatus::WARN_SHADOWING_DOMAIN);
1442       }
1443     }
1444 
1445     if (!access_result.status.IsInclude()) {
1446       if (options.return_excluded_cookies()) {
1447         excluded_cookies->push_back({*cookie_ptr, access_result});
1448       }
1449       continue;
1450     }
1451 
1452     if (options.update_access_time()) {
1453       InternalUpdateCookieAccessTime(cookie_ptr, current_time);
1454     }
1455 
1456     included_cookies->push_back({*cookie_ptr, access_result});
1457   }
1458 }
1459 
MaybeDeleteEquivalentCookieAndUpdateStatus(const std::string & key,const CanonicalCookie & cookie_being_set,bool allowed_to_set_secure_cookie,bool skip_httponly,bool already_expired,base::Time * creation_date_to_inherit,CookieInclusionStatus * status,std::optional<PartitionedCookieMap::iterator> cookie_partition_it)1460 void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
1461     const std::string& key,
1462     const CanonicalCookie& cookie_being_set,
1463     bool allowed_to_set_secure_cookie,
1464     bool skip_httponly,
1465     bool already_expired,
1466     base::Time* creation_date_to_inherit,
1467     CookieInclusionStatus* status,
1468     std::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
1469   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1470   DCHECK(!status->HasExclusionReason(
1471       CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE));
1472   DCHECK(!status->HasExclusionReason(
1473       CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY));
1474 
1475   CookieMap* cookie_map = &cookies_;
1476   if (cookie_partition_it) {
1477     cookie_map = cookie_partition_it.value()->second.get();
1478   }
1479 
1480   bool found_equivalent_cookie = false;
1481   CookieMap::iterator deletion_candidate_it = cookie_map->end();
1482   CanonicalCookie* skipped_secure_cookie = nullptr;
1483 
1484   // Check every cookie matching this domain key for equivalence.
1485   CookieMapItPair range_its = cookie_map->equal_range(key);
1486   for (auto cur_it = range_its.first; cur_it != range_its.second; ++cur_it) {
1487     CanonicalCookie* cur_existing_cookie = cur_it->second.get();
1488 
1489     // Evaluate "Leave Secure Cookies Alone":
1490     // If the cookie is being set from an insecure source, then if an
1491     // "equivalent" Secure cookie already exists, then the cookie should *not*
1492     // be updated.
1493     //
1494     // "Equivalent" means they are the same by
1495     // IsEquivalentForSecureCookieMatching(). See the comment there for
1496     // details. (Note this is not a symmetric comparison.) This notion of
1497     // equivalence is slightly more inclusive than the usual IsEquivalent() one.
1498     //
1499     // See: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone
1500     if (cur_existing_cookie->SecureAttribute() &&
1501         !allowed_to_set_secure_cookie &&
1502         cookie_being_set.IsEquivalentForSecureCookieMatching(
1503             *cur_existing_cookie)) {
1504       // Hold onto this for additional Netlogging later if we end up preserving
1505       // a would-have-been-deleted cookie because of this.
1506       skipped_secure_cookie = cur_existing_cookie;
1507       net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_SECURE,
1508                         [&](NetLogCaptureMode capture_mode) {
1509                           return NetLogCookieMonsterCookieRejectedSecure(
1510                               skipped_secure_cookie, &cookie_being_set,
1511                               capture_mode);
1512                         });
1513       status->AddExclusionReason(
1514           CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE);
1515     }
1516 
1517     if (cookie_being_set.IsEquivalent(*cur_existing_cookie)) {
1518       // We should never have more than one equivalent cookie, since they should
1519       // overwrite each other.
1520       CHECK(!found_equivalent_cookie)
1521           << "Duplicate equivalent cookies found, cookie store is corrupted.";
1522       DCHECK(deletion_candidate_it == cookie_map->end());
1523       found_equivalent_cookie = true;
1524 
1525       // The |cookie_being_set| is rejected for trying to overwrite an httponly
1526       // cookie when it should not be able to.
1527       if (skip_httponly && cur_existing_cookie->IsHttpOnly()) {
1528         net_log_.AddEvent(
1529             NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_HTTPONLY,
1530             [&](NetLogCaptureMode capture_mode) {
1531               return NetLogCookieMonsterCookieRejectedHttponly(
1532                   cur_existing_cookie, &cookie_being_set, capture_mode);
1533             });
1534         status->AddExclusionReason(
1535             CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY);
1536       } else {
1537         deletion_candidate_it = cur_it;
1538       }
1539     }
1540   }
1541 
1542   if (deletion_candidate_it != cookie_map->end()) {
1543     CanonicalCookie* deletion_candidate = deletion_candidate_it->second.get();
1544     if (deletion_candidate->Value() == cookie_being_set.Value())
1545       *creation_date_to_inherit = deletion_candidate->CreationDate();
1546     if (status->IsInclude()) {
1547       if (cookie_being_set.IsPartitioned()) {
1548         InternalDeletePartitionedCookie(
1549             cookie_partition_it.value(), deletion_candidate_it,
1550             true /* sync_to_store */,
1551             already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
1552                             : DELETE_COOKIE_OVERWRITE);
1553       } else {
1554         InternalDeleteCookie(deletion_candidate_it, true /* sync_to_store */,
1555                              already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
1556                                              : DELETE_COOKIE_OVERWRITE);
1557       }
1558     } else if (status->HasExclusionReason(
1559                    CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE)) {
1560       // Log that we preserved a cookie that would have been deleted due to
1561       // Leave Secure Cookies Alone. This arbitrarily only logs the last
1562       // |skipped_secure_cookie| that we were left with after the for loop, even
1563       // if there were multiple matching Secure cookies that were left alone.
1564       DCHECK(skipped_secure_cookie);
1565       net_log_.AddEvent(
1566           NetLogEventType::COOKIE_STORE_COOKIE_PRESERVED_SKIPPED_SECURE,
1567           [&](NetLogCaptureMode capture_mode) {
1568             return NetLogCookieMonsterCookiePreservedSkippedSecure(
1569                 skipped_secure_cookie, deletion_candidate, &cookie_being_set,
1570                 capture_mode);
1571           });
1572     }
1573   }
1574 }
1575 
InternalInsertCookie(const std::string & key,std::unique_ptr<CanonicalCookie> cc,bool sync_to_store,const CookieAccessResult & access_result,bool dispatch_change)1576 CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
1577     const std::string& key,
1578     std::unique_ptr<CanonicalCookie> cc,
1579     bool sync_to_store,
1580     const CookieAccessResult& access_result,
1581     bool dispatch_change) {
1582   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1583   CanonicalCookie* cc_ptr = cc.get();
1584 
1585   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
1586                     [&](NetLogCaptureMode capture_mode) {
1587                       return NetLogCookieMonsterCookieAdded(
1588                           cc.get(), sync_to_store, capture_mode);
1589                     });
1590   if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
1591     store_->AddCookie(*cc_ptr);
1592 
1593   auto inserted = cookies_.insert(CookieMap::value_type(key, std::move(cc)));
1594 
1595   LogStoredCookieToUMA(*cc_ptr, access_result);
1596 
1597   DCHECK(access_result.status.IsInclude());
1598   if (dispatch_change) {
1599     change_dispatcher_.DispatchChange(
1600         CookieChangeInfo(*cc_ptr, access_result, CookieChangeCause::INSERTED),
1601         true);
1602   }
1603 
1604   // If this is the first cookie in |cookies_| with this key, increment the
1605   // |num_keys_| counter.
1606   bool different_prev =
1607       inserted == cookies_.begin() || std::prev(inserted)->first != key;
1608   // According to std::multiqueue documentation:
1609   // "If the container has elements with equivalent key, inserts at the upper
1610   // bound of that range. (since C++11)"
1611   // This means that "inserted" iterator either points to the last element in
1612   // the map, or the element succeeding it has to have different key.
1613   DCHECK(std::next(inserted) == cookies_.end() ||
1614          std::next(inserted)->first != key);
1615   if (different_prev)
1616     ++num_keys_;
1617 
1618   return inserted;
1619 }
1620 
ShouldUpdatePersistentStore(CanonicalCookie * cc)1621 bool CookieMonster::ShouldUpdatePersistentStore(CanonicalCookie* cc) {
1622   return (cc->IsPersistent() || persist_session_cookies_) && store_.get();
1623 }
1624 
1625 CookieMonster::PartitionedCookieMapIterators
InternalInsertPartitionedCookie(std::string key,std::unique_ptr<CanonicalCookie> cc,bool sync_to_store,const CookieAccessResult & access_result,bool dispatch_change)1626 CookieMonster::InternalInsertPartitionedCookie(
1627     std::string key,
1628     std::unique_ptr<CanonicalCookie> cc,
1629     bool sync_to_store,
1630     const CookieAccessResult& access_result,
1631     bool dispatch_change) {
1632   DCHECK(cc->IsPartitioned());
1633   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1634   CanonicalCookie* cc_ptr = cc.get();
1635 
1636   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
1637                     [&](NetLogCaptureMode capture_mode) {
1638                       return NetLogCookieMonsterCookieAdded(
1639                           cc.get(), sync_to_store, capture_mode);
1640                     });
1641   if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
1642     store_->AddCookie(*cc_ptr);
1643 
1644   CookiePartitionKey partition_key(cc->PartitionKey().value());
1645 
1646   size_t n_bytes = NameValueSizeBytes(*cc);
1647   num_partitioned_cookies_bytes_ += n_bytes;
1648   bytes_per_cookie_partition_[partition_key] += n_bytes;
1649   if (partition_key.nonce()) {
1650     num_nonced_partitioned_cookie_bytes_ += n_bytes;
1651   }
1652 
1653   PartitionedCookieMap::iterator partition_it =
1654       partitioned_cookies_.find(partition_key);
1655   if (partition_it == partitioned_cookies_.end()) {
1656     partition_it =
1657         partitioned_cookies_
1658             .insert(PartitionedCookieMap::value_type(
1659                 std::move(partition_key), std::make_unique<CookieMap>()))
1660             .first;
1661   }
1662 
1663   CookieMap::iterator cookie_it = partition_it->second->insert(
1664       CookieMap::value_type(std::move(key), std::move(cc)));
1665   ++num_partitioned_cookies_;
1666   if (partition_it->first.nonce()) {
1667     ++num_nonced_partitioned_cookies_;
1668   }
1669   CHECK_GE(num_partitioned_cookies_, num_nonced_partitioned_cookies_);
1670 
1671   LogStoredCookieToUMA(*cc_ptr, access_result);
1672 
1673   DCHECK(access_result.status.IsInclude());
1674   if (dispatch_change) {
1675     change_dispatcher_.DispatchChange(
1676         CookieChangeInfo(*cc_ptr, access_result, CookieChangeCause::INSERTED),
1677         true);
1678   }
1679 
1680   return std::pair(partition_it, cookie_it);
1681 }
1682 
SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,const GURL & source_url,const CookieOptions & options,SetCookiesCallback callback,std::optional<CookieAccessResult> cookie_access_result)1683 void CookieMonster::SetCanonicalCookie(
1684     std::unique_ptr<CanonicalCookie> cc,
1685     const GURL& source_url,
1686     const CookieOptions& options,
1687     SetCookiesCallback callback,
1688     std::optional<CookieAccessResult> cookie_access_result) {
1689   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1690 // TODO(crbug.com/1482799): Fix macos specific issue with CHECK_IS_TEST crashing
1691 // network service process.
1692 #if !BUILDFLAG(IS_MAC)
1693   // Only tests should be adding new cookies with source type kUnknown. If this
1694   // line causes a fatal track down the callsite and have it correctly set the
1695   // source type to kOther (or kHTTP/kScript where applicable). See
1696   // CookieSourceType in net/cookies/cookie_constants.h for more.
1697   if (cc->SourceType() == CookieSourceType::kUnknown) {
1698     CHECK_IS_TEST(base::NotFatalUntil::M126);
1699   }
1700 #endif
1701 
1702   bool delegate_treats_url_as_trustworthy =
1703       cookie_access_delegate() &&
1704       cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(source_url);
1705 
1706   CookieAccessResult access_result = cc->IsSetPermittedInContext(
1707       source_url, options,
1708       CookieAccessParams(GetAccessSemanticsForCookie(*cc),
1709                          delegate_treats_url_as_trustworthy),
1710       cookieable_schemes_, cookie_access_result);
1711 
1712   const std::string key(GetKey(cc->Domain()));
1713 
1714   base::Time creation_date = cc->CreationDate();
1715   if (creation_date.is_null()) {
1716     creation_date = Time::Now();
1717     cc->SetCreationDate(creation_date);
1718   }
1719   bool already_expired = cc->IsExpired(creation_date);
1720 
1721   base::Time creation_date_to_inherit;
1722 
1723   std::optional<PartitionedCookieMap::iterator> cookie_partition_it;
1724   bool should_try_to_delete_duplicates = true;
1725 
1726   if (cc->IsPartitioned()) {
1727     auto it = partitioned_cookies_.find(cc->PartitionKey().value());
1728     if (it == partitioned_cookies_.end()) {
1729       // This is the first cookie in its partition, so it won't have any
1730       // duplicates.
1731       should_try_to_delete_duplicates = false;
1732     } else {
1733       cookie_partition_it = std::make_optional(it);
1734     }
1735   }
1736 
1737   // Iterates through existing cookies for the same eTLD+1, and potentially
1738   // deletes an existing cookie, so any ExclusionReasons in |status| that would
1739   // prevent such deletion should be finalized beforehand.
1740   if (should_try_to_delete_duplicates) {
1741     MaybeDeleteEquivalentCookieAndUpdateStatus(
1742         key, *cc, access_result.is_allowed_to_access_secure_cookies,
1743         options.exclude_httponly(), already_expired, &creation_date_to_inherit,
1744         &access_result.status, cookie_partition_it);
1745   }
1746 
1747   if (access_result.status.HasExclusionReason(
1748           CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE) ||
1749       access_result.status.HasExclusionReason(
1750           CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY)) {
1751     DVLOG(net::cookie_util::kVlogSetCookies)
1752         << "SetCookie() not clobbering httponly cookie or secure cookie for "
1753            "insecure scheme";
1754   }
1755 
1756   if (access_result.status.IsInclude()) {
1757     DVLOG(net::cookie_util::kVlogSetCookies)
1758         << "SetCookie() key: " << key << " cc: " << cc->DebugString();
1759 
1760     if (cc->IsEffectivelySameSiteNone()) {
1761       size_t cookie_size = NameValueSizeBytes(*cc);
1762       UMA_HISTOGRAM_COUNTS_10000("Cookie.SameSiteNoneSizeBytes", cookie_size);
1763       if (cc->IsPartitioned()) {
1764         UMA_HISTOGRAM_COUNTS_10000("Cookie.SameSiteNoneSizeBytes.Partitioned",
1765                                    cookie_size);
1766       } else {
1767         UMA_HISTOGRAM_COUNTS_10000("Cookie.SameSiteNoneSizeBytes.Unpartitioned",
1768                                    cookie_size);
1769       }
1770     }
1771 
1772     std::optional<CookiePartitionKey> cookie_partition_key = cc->PartitionKey();
1773     CHECK_EQ(cc->IsPartitioned(), cookie_partition_key.has_value());
1774 
1775     // Realize that we might be setting an expired cookie, and the only point
1776     // was to delete the cookie which we've already done.
1777     if (!already_expired) {
1778       HistogramExpirationDuration(*cc, creation_date);
1779 
1780       // Histogram the type of scheme used on URLs that set cookies. This
1781       // intentionally includes cookies that are set or overwritten by
1782       // http:// URLs, but not cookies that are cleared by http:// URLs, to
1783       // understand if the former behavior can be deprecated for Secure
1784       // cookies.
1785       // TODO(crbug.com/993120): Consider removing this histogram. The decision
1786       // it was added to evaluate has been implemented and standardized.
1787       CookieSource cookie_source_sample =
1788           (source_url.SchemeIsCryptographic()
1789                ? (cc->SecureAttribute()
1790                       ? CookieSource::kSecureCookieCryptographicScheme
1791                       : CookieSource::kNonsecureCookieCryptographicScheme)
1792                : (cc->SecureAttribute()
1793                       ? CookieSource::kSecureCookieNoncryptographicScheme
1794                       : CookieSource::kNonsecureCookieNoncryptographicScheme));
1795       UMA_HISTOGRAM_ENUMERATION("Cookie.CookieSourceScheme",
1796                                 cookie_source_sample);
1797 
1798       UMA_HISTOGRAM_BOOLEAN("Cookie.DomainSet", cc->IsDomainCookie());
1799 
1800       if (!creation_date_to_inherit.is_null()) {
1801         cc->SetCreationDate(creation_date_to_inherit);
1802       }
1803 
1804       if (cookie_partition_key.has_value()) {
1805         InternalInsertPartitionedCookie(key, std::move(cc), true,
1806                                         access_result);
1807       } else {
1808         InternalInsertCookie(key, std::move(cc), true, access_result);
1809       }
1810     } else {
1811       DVLOG(net::cookie_util::kVlogSetCookies)
1812           << "SetCookie() not storing already expired cookie.";
1813     }
1814 
1815     // We assume that hopefully setting a cookie will be less common than
1816     // querying a cookie.  Since setting a cookie can put us over our limits,
1817     // make sure that we garbage collect...  We can also make the assumption
1818     // that if a cookie was set, in the common case it will be used soon after,
1819     // and we will purge the expired cookies in GetCookies().
1820     if (cookie_partition_key.has_value()) {
1821       GarbageCollectPartitionedCookies(creation_date,
1822                                        cookie_partition_key.value(), key);
1823     } else {
1824       GarbageCollect(creation_date, key);
1825     }
1826 
1827     if (IsLocalhost(source_url)) {
1828       UMA_HISTOGRAM_ENUMERATION(
1829           "Cookie.Port.Set.Localhost",
1830           ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort()));
1831     } else {
1832       UMA_HISTOGRAM_ENUMERATION(
1833           "Cookie.Port.Set.RemoteHost",
1834           ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort()));
1835     }
1836 
1837     UMA_HISTOGRAM_ENUMERATION("Cookie.CookieSourceSchemeName",
1838                               GetSchemeNameEnum(source_url));
1839   } else {
1840     // If the cookie would be excluded, don't bother warning about the 3p cookie
1841     // phaseout.
1842     access_result.status.RemoveWarningReason(
1843         net::CookieInclusionStatus::WARN_THIRD_PARTY_PHASEOUT);
1844   }
1845 
1846   // TODO(chlily): Log metrics.
1847   MaybeRunCookieCallback(std::move(callback), access_result);
1848 }
1849 
SetAllCookies(CookieList list,SetCookiesCallback callback)1850 void CookieMonster::SetAllCookies(CookieList list,
1851                                   SetCookiesCallback callback) {
1852   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1853 
1854   // Nuke the existing store.
1855   while (!cookies_.empty()) {
1856     // TODO(rdsmith): The CANONICAL is a lie.
1857     InternalDeleteCookie(cookies_.begin(), true, DELETE_COOKIE_EXPLICIT);
1858   }
1859 
1860   // Set all passed in cookies.
1861   for (const auto& cookie : list) {
1862     const std::string key(GetKey(cookie.Domain()));
1863     Time creation_time = cookie.CreationDate();
1864     if (cookie.IsExpired(creation_time))
1865       continue;
1866 
1867     HistogramExpirationDuration(cookie, creation_time);
1868 
1869     CookieAccessResult access_result;
1870     access_result.access_semantics = GetAccessSemanticsForCookie(cookie);
1871 
1872     if (cookie.IsPartitioned()) {
1873       InternalInsertPartitionedCookie(
1874           key, std::make_unique<CanonicalCookie>(cookie), true, access_result);
1875       GarbageCollectPartitionedCookies(creation_time,
1876                                        cookie.PartitionKey().value(), key);
1877     } else {
1878       InternalInsertCookie(key, std::make_unique<CanonicalCookie>(cookie), true,
1879                            access_result);
1880       GarbageCollect(creation_time, key);
1881     }
1882   }
1883 
1884   // TODO(rdsmith): If this function always returns the same value, it
1885   // shouldn't have a return value.  But it should also be deleted (see
1886   // https://codereview.chromium.org/2882063002/#msg64), which would
1887   // solve the return value problem.
1888   MaybeRunCookieCallback(std::move(callback), CookieAccessResult());
1889 }
1890 
InternalUpdateCookieAccessTime(CanonicalCookie * cc,const Time & current)1891 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
1892                                                    const Time& current) {
1893   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1894 
1895   // Based off the Mozilla code.  When a cookie has been accessed recently,
1896   // don't bother updating its access time again.  This reduces the number of
1897   // updates we do during pageload, which in turn reduces the chance our storage
1898   // backend will hit its batch thresholds and be forced to update.
1899   if ((current - cc->LastAccessDate()) < last_access_threshold_)
1900     return;
1901 
1902   cc->SetLastAccessDate(current);
1903   if (ShouldUpdatePersistentStore(cc))
1904     store_->UpdateCookieAccessTime(*cc);
1905 }
1906 
1907 // InternalDeleteCookies must not invalidate iterators other than the one being
1908 // deleted.
InternalDeleteCookie(CookieMap::iterator it,bool sync_to_store,DeletionCause deletion_cause)1909 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
1910                                          bool sync_to_store,
1911                                          DeletionCause deletion_cause) {
1912   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1913 
1914   // Ideally, this would be asserted up where we define kChangeCauseMapping,
1915   // but DeletionCause's visibility (or lack thereof) forces us to make
1916   // this check here.
1917   static_assert(std::size(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1918                 "kChangeCauseMapping size should match DeletionCause size");
1919 
1920   CanonicalCookie* cc = it->second.get();
1921   DVLOG(net::cookie_util::kVlogSetCookies)
1922       << "InternalDeleteCookie()"
1923       << ", cause:" << deletion_cause << ", cc: " << cc->DebugString();
1924 
1925   ChangeCausePair mapping = kChangeCauseMapping[deletion_cause];
1926   if (deletion_cause != DELETE_COOKIE_DONT_RECORD) {
1927     net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_DELETED,
1928                       [&](NetLogCaptureMode capture_mode) {
1929                         return NetLogCookieMonsterCookieDeleted(
1930                             cc, mapping.cause, sync_to_store, capture_mode);
1931                       });
1932   }
1933 
1934   if (ShouldUpdatePersistentStore(cc) && sync_to_store)
1935     store_->DeleteCookie(*cc);
1936 
1937   change_dispatcher_.DispatchChange(
1938       CookieChangeInfo(
1939           *cc,
1940           CookieAccessResult(CookieEffectiveSameSite::UNDEFINED,
1941                              CookieInclusionStatus(),
1942                              GetAccessSemanticsForCookie(*cc),
1943                              true /* is_allowed_to_access_secure_cookies */),
1944           mapping.cause),
1945       mapping.notify);
1946 
1947   // If this is the last cookie in |cookies_| with this key, decrement the
1948   // |num_keys_| counter.
1949   bool different_prev =
1950       it == cookies_.begin() || std::prev(it)->first != it->first;
1951   bool different_next =
1952       std::next(it) == cookies_.end() || std::next(it)->first != it->first;
1953   if (different_prev && different_next)
1954     --num_keys_;
1955 
1956   DCHECK(cookies_.find(it->first) != cookies_.end())
1957       << "Called erase with an iterator not in the cookie map";
1958   cookies_.erase(it);
1959 }
1960 
InternalDeletePartitionedCookie(PartitionedCookieMap::iterator partition_it,CookieMap::iterator cookie_it,bool sync_to_store,DeletionCause deletion_cause)1961 void CookieMonster::InternalDeletePartitionedCookie(
1962     PartitionedCookieMap::iterator partition_it,
1963     CookieMap::iterator cookie_it,
1964     bool sync_to_store,
1965     DeletionCause deletion_cause) {
1966   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1967 
1968   // Ideally, this would be asserted up where we define kChangeCauseMapping,
1969   // but DeletionCause's visibility (or lack thereof) forces us to make
1970   // this check here.
1971   static_assert(std::size(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1972                 "kChangeCauseMapping size should match DeletionCause size");
1973 
1974   CanonicalCookie* cc = cookie_it->second.get();
1975   DCHECK(cc->IsPartitioned());
1976   DVLOG(net::cookie_util::kVlogSetCookies)
1977       << "InternalDeletePartitionedCookie()"
1978       << ", cause:" << deletion_cause << ", cc: " << cc->DebugString();
1979 
1980   ChangeCausePair mapping = kChangeCauseMapping[deletion_cause];
1981   if (deletion_cause != DELETE_COOKIE_DONT_RECORD) {
1982     net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_DELETED,
1983                       [&](NetLogCaptureMode capture_mode) {
1984                         return NetLogCookieMonsterCookieDeleted(
1985                             cc, mapping.cause, sync_to_store, capture_mode);
1986                       });
1987   }
1988 
1989   if (ShouldUpdatePersistentStore(cc) && sync_to_store)
1990     store_->DeleteCookie(*cc);
1991 
1992   change_dispatcher_.DispatchChange(
1993       CookieChangeInfo(
1994           *cc,
1995           CookieAccessResult(CookieEffectiveSameSite::UNDEFINED,
1996                              CookieInclusionStatus(),
1997                              GetAccessSemanticsForCookie(*cc),
1998                              true /* is_allowed_to_access_secure_cookies */),
1999           mapping.cause),
2000       mapping.notify);
2001 
2002   size_t n_bytes = NameValueSizeBytes(*cc);
2003   num_partitioned_cookies_bytes_ -= n_bytes;
2004   bytes_per_cookie_partition_[*cc->PartitionKey()] -= n_bytes;
2005   if (CookiePartitionKey::HasNonce(cc->PartitionKey())) {
2006     num_nonced_partitioned_cookie_bytes_ -= n_bytes;
2007   }
2008 
2009   DCHECK(partition_it->second->find(cookie_it->first) !=
2010          partition_it->second->end())
2011       << "Called erase with an iterator not in this partitioned cookie map";
2012   partition_it->second->erase(cookie_it);
2013   --num_partitioned_cookies_;
2014   if (partition_it->first.nonce()) {
2015     --num_nonced_partitioned_cookies_;
2016   }
2017   CHECK_GE(num_partitioned_cookies_, num_nonced_partitioned_cookies_);
2018 
2019   if (partition_it->second->empty())
2020     partitioned_cookies_.erase(partition_it);
2021 }
2022 
2023 // Domain expiry behavior is unchanged by key/expiry scheme (the
2024 // meaning of the key is different, but that's not visible to this routine).
GarbageCollect(const Time & current,const std::string & key)2025 size_t CookieMonster::GarbageCollect(const Time& current,
2026                                      const std::string& key) {
2027   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2028 
2029   size_t num_deleted = 0;
2030   const Time safe_date(Time::Now() - base::Days(kSafeFromGlobalPurgeDays));
2031 
2032   const bool obc_behavior_enabled =
2033       cookie_util::IsOriginBoundCookiesPartiallyEnabled();
2034 
2035   // Collect garbage for this key, minding cookie priorities.
2036   if (cookies_.count(key) > kDomainMaxCookies) {
2037     DVLOG(net::cookie_util::kVlogGarbageCollection)
2038         << "GarbageCollect() key: " << key;
2039 
2040     CookieItVector* cookie_its;
2041 
2042     CookieItVector non_expired_cookie_its;
2043     cookie_its = &non_expired_cookie_its;
2044     num_deleted +=
2045         GarbageCollectExpired(current, cookies_.equal_range(key), cookie_its);
2046 
2047     if (cookie_its->size() > kDomainMaxCookies) {
2048       DVLOG(net::cookie_util::kVlogGarbageCollection)
2049           << "Deep Garbage Collect domain.";
2050 
2051       if (domain_purged_keys_.size() < kMaxDomainPurgedKeys)
2052         domain_purged_keys_.insert(key);
2053 
2054       size_t purge_goal =
2055           cookie_its->size() - (kDomainMaxCookies - kDomainPurgeCookies);
2056       DCHECK(purge_goal > kDomainPurgeCookies);
2057 
2058       // Sort the cookies by access date, from least-recent to most-recent.
2059       std::sort(cookie_its->begin(), cookie_its->end(), LRACookieSorter);
2060 
2061       CookieItList cookie_it_list;
2062       if (obc_behavior_enabled) {
2063         cookie_it_list = CookieItList(cookie_its->begin(), cookie_its->end());
2064       }
2065 
2066       // Remove all but the kDomainCookiesQuotaLow most-recently accessed
2067       // cookies with low-priority. Then, if cookies still need to be removed,
2068       // bump the quota and remove low- and medium-priority. Then, if cookies
2069       // _still_ need to be removed, bump the quota and remove cookies with
2070       // any priority.
2071       //
2072       // 1.  Low-priority non-secure cookies.
2073       // 2.  Low-priority secure cookies.
2074       // 3.  Medium-priority non-secure cookies.
2075       // 4.  High-priority non-secure cookies.
2076       // 5.  Medium-priority secure cookies.
2077       // 6.  High-priority secure cookies.
2078       constexpr struct {
2079         CookiePriority priority;
2080         bool protect_secure_cookies;
2081       } kPurgeRounds[] = {
2082           // 1.  Low-priority non-secure cookies.
2083           {COOKIE_PRIORITY_LOW, true},
2084           // 2.  Low-priority secure cookies.
2085           {COOKIE_PRIORITY_LOW, false},
2086           // 3.  Medium-priority non-secure cookies.
2087           {COOKIE_PRIORITY_MEDIUM, true},
2088           // 4.  High-priority non-secure cookies.
2089           {COOKIE_PRIORITY_HIGH, true},
2090           // 5.  Medium-priority secure cookies.
2091           {COOKIE_PRIORITY_MEDIUM, false},
2092           // 6.  High-priority secure cookies.
2093           {COOKIE_PRIORITY_HIGH, false},
2094       };
2095 
2096       size_t quota = 0;
2097       for (const auto& purge_round : kPurgeRounds) {
2098         // Adjust quota according to the priority of cookies. Each round should
2099         // protect certain number of cookies in order to avoid starvation.
2100         // For example, when each round starts to remove cookies, the number of
2101         // cookies of that priority are counted and a decision whether they
2102         // should be deleted or not is made. If yes, some number of cookies of
2103         // that priority are deleted considering the quota.
2104         switch (purge_round.priority) {
2105           case COOKIE_PRIORITY_LOW:
2106             quota = kDomainCookiesQuotaLow;
2107             break;
2108           case COOKIE_PRIORITY_MEDIUM:
2109             quota = kDomainCookiesQuotaMedium;
2110             break;
2111           case COOKIE_PRIORITY_HIGH:
2112             quota = kDomainCookiesQuotaHigh;
2113             break;
2114         }
2115         size_t just_deleted = 0u;
2116         // Purge up to |purge_goal| for all cookies at the given priority.  This
2117         // path will be taken only if the initial non-secure purge did not evict
2118         // enough cookies.
2119         if (purge_goal > 0) {
2120           if (obc_behavior_enabled) {
2121             just_deleted = PurgeLeastRecentMatchesForOBC(
2122                 &cookie_it_list, purge_round.priority, quota, purge_goal,
2123                 !purge_round.protect_secure_cookies);
2124           } else {
2125             just_deleted = PurgeLeastRecentMatches(
2126                 cookie_its, purge_round.priority, quota, purge_goal,
2127                 purge_round.protect_secure_cookies);
2128           }
2129           DCHECK_LE(just_deleted, purge_goal);
2130           purge_goal -= just_deleted;
2131           num_deleted += just_deleted;
2132         }
2133       }
2134 
2135       DCHECK_EQ(0u, purge_goal);
2136     }
2137   }
2138 
2139   // Collect garbage for everything. With firefox style we want to preserve
2140   // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict.
2141   if (cookies_.size() > kMaxCookies && earliest_access_time_ < safe_date) {
2142     DVLOG(net::cookie_util::kVlogGarbageCollection)
2143         << "GarbageCollect() everything";
2144     CookieItVector cookie_its;
2145 
2146     num_deleted += GarbageCollectExpired(
2147         current, CookieMapItPair(cookies_.begin(), cookies_.end()),
2148         &cookie_its);
2149 
2150     if (cookie_its.size() > kMaxCookies) {
2151       DVLOG(net::cookie_util::kVlogGarbageCollection)
2152           << "Deep Garbage Collect everything.";
2153       size_t purge_goal = cookie_its.size() - (kMaxCookies - kPurgeCookies);
2154       DCHECK(purge_goal > kPurgeCookies);
2155 
2156       CookieItVector secure_cookie_its;
2157       CookieItVector non_secure_cookie_its;
2158       SplitCookieVectorIntoSecureAndNonSecure(cookie_its, &secure_cookie_its,
2159                                               &non_secure_cookie_its);
2160       size_t non_secure_purge_goal =
2161           std::min<size_t>(purge_goal, non_secure_cookie_its.size());
2162 
2163       base::Time earliest_non_secure_access_time;
2164       size_t just_deleted = GarbageCollectLeastRecentlyAccessed(
2165           current, safe_date, non_secure_purge_goal, non_secure_cookie_its,
2166           &earliest_non_secure_access_time);
2167       num_deleted += just_deleted;
2168 
2169       if (secure_cookie_its.size() == 0) {
2170         // This case is unlikely, but should still update
2171         // |earliest_access_time_| if only have non-secure cookies.
2172         earliest_access_time_ = earliest_non_secure_access_time;
2173         // Garbage collection can't delete all cookies.
2174         DCHECK(!earliest_access_time_.is_null());
2175       } else if (just_deleted < purge_goal) {
2176         size_t secure_purge_goal = std::min<size_t>(purge_goal - just_deleted,
2177                                                     secure_cookie_its.size());
2178         base::Time earliest_secure_access_time;
2179         num_deleted += GarbageCollectLeastRecentlyAccessed(
2180             current, safe_date, secure_purge_goal, secure_cookie_its,
2181             &earliest_secure_access_time);
2182 
2183         if (!earliest_non_secure_access_time.is_null() &&
2184             earliest_non_secure_access_time < earliest_secure_access_time) {
2185           earliest_access_time_ = earliest_non_secure_access_time;
2186         } else {
2187           earliest_access_time_ = earliest_secure_access_time;
2188         }
2189 
2190         // Garbage collection can't delete all cookies.
2191         DCHECK(!earliest_access_time_.is_null());
2192       }
2193 
2194       // If there are secure cookies, but deleting non-secure cookies was enough
2195       // to meet the purge goal, secure cookies are never examined, so
2196       // |earliest_access_time_| can't be determined. Leaving it alone will mean
2197       // it's no later than the real earliest last access time, so this won't
2198       // lead to any problems.
2199     }
2200   }
2201 
2202   return num_deleted;
2203 }
2204 
GarbageCollectPartitionedCookies(const base::Time & current,const CookiePartitionKey & cookie_partition_key,const std::string & key)2205 size_t CookieMonster::GarbageCollectPartitionedCookies(
2206     const base::Time& current,
2207     const CookiePartitionKey& cookie_partition_key,
2208     const std::string& key) {
2209   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2210 
2211   size_t num_deleted = 0;
2212   PartitionedCookieMap::iterator cookie_partition_it =
2213       partitioned_cookies_.find(cookie_partition_key);
2214 
2215   if (cookie_partition_it == partitioned_cookies_.end())
2216     return num_deleted;
2217 
2218   if (NumBytesInCookieMapForKey(*cookie_partition_it->second.get(), key) >
2219           kPerPartitionDomainMaxCookieBytes ||
2220       cookie_partition_it->second->count(key) > kPerPartitionDomainMaxCookies) {
2221     // TODO(crbug.com/1225444): Log garbage collection for partitioned cookies.
2222 
2223     CookieItVector non_expired_cookie_its;
2224     num_deleted += GarbageCollectExpiredPartitionedCookies(
2225         current, cookie_partition_it,
2226         cookie_partition_it->second->equal_range(key), &non_expired_cookie_its);
2227 
2228     size_t bytes_used = NumBytesInCookieItVector(non_expired_cookie_its);
2229 
2230     if (bytes_used > kPerPartitionDomainMaxCookieBytes ||
2231         non_expired_cookie_its.size() > kPerPartitionDomainMaxCookies) {
2232       // TODO(crbug.com/1225444): Log deep garbage collection for partitioned
2233       // cookies.
2234       std::sort(non_expired_cookie_its.begin(), non_expired_cookie_its.end(),
2235                 LRACookieSorter);
2236 
2237       for (size_t i = 0;
2238            bytes_used > kPerPartitionDomainMaxCookieBytes ||
2239            non_expired_cookie_its.size() - i > kPerPartitionDomainMaxCookies;
2240            ++i) {
2241         bytes_used -= NameValueSizeBytes(*non_expired_cookie_its[i]->second);
2242         InternalDeletePartitionedCookie(
2243             cookie_partition_it, non_expired_cookie_its[i], true,
2244             DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN);
2245         ++num_deleted;
2246       }
2247     }
2248   }
2249 
2250   // TODO(crbug.com/1225444): Enforce global limit on partitioned cookies.
2251 
2252   return num_deleted;
2253 }
2254 
PurgeLeastRecentMatches(CookieItVector * cookies,CookiePriority priority,size_t to_protect,size_t purge_goal,bool protect_secure_cookies)2255 size_t CookieMonster::PurgeLeastRecentMatches(CookieItVector* cookies,
2256                                               CookiePriority priority,
2257                                               size_t to_protect,
2258                                               size_t purge_goal,
2259                                               bool protect_secure_cookies) {
2260   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2261 
2262   // 1. Count number of the cookies at |priority|
2263   size_t cookies_count_possibly_to_be_deleted = CountCookiesForPossibleDeletion(
2264       priority, cookies, false /* count all cookies */);
2265 
2266   // 2. If |cookies_count_possibly_to_be_deleted| at |priority| is less than or
2267   // equal |to_protect|, skip round in order to preserve the quota. This
2268   // involves secure and non-secure cookies at |priority|.
2269   if (cookies_count_possibly_to_be_deleted <= to_protect)
2270     return 0u;
2271 
2272   // 3. Calculate number of secure cookies at |priority|
2273   // and number of cookies at |priority| that can possibly be deleted.
2274   // It is guaranteed we do not delete more than |purge_goal| even if
2275   // |cookies_count_possibly_to_be_deleted| is higher.
2276   size_t secure_cookies = 0u;
2277   if (protect_secure_cookies) {
2278     secure_cookies = CountCookiesForPossibleDeletion(
2279         priority, cookies, protect_secure_cookies /* count secure cookies */);
2280     cookies_count_possibly_to_be_deleted -=
2281         std::max(secure_cookies, to_protect);
2282   } else {
2283     cookies_count_possibly_to_be_deleted -= to_protect;
2284   }
2285 
2286   size_t removed = 0u;
2287   size_t current = 0u;
2288   while ((removed < purge_goal && current < cookies->size()) &&
2289          cookies_count_possibly_to_be_deleted > 0) {
2290     const CanonicalCookie* current_cookie = cookies->at(current)->second.get();
2291     // Only delete the current cookie if the priority is equal to
2292     // the current level.
2293     if (IsCookieEligibleForEviction(priority, protect_secure_cookies,
2294                                     current_cookie)) {
2295       InternalDeleteCookie(cookies->at(current), true,
2296                            DELETE_COOKIE_EVICTED_DOMAIN);
2297       cookies->erase(cookies->begin() + current);
2298       removed++;
2299       cookies_count_possibly_to_be_deleted--;
2300     } else {
2301       current++;
2302     }
2303   }
2304   return removed;
2305 }
2306 
PurgeLeastRecentMatchesForOBC(CookieItList * cookies,CookiePriority priority,size_t to_protect,size_t purge_goal,bool delete_secure_cookies)2307 size_t CookieMonster::PurgeLeastRecentMatchesForOBC(
2308     CookieItList* cookies,
2309     CookiePriority priority,
2310     size_t to_protect,
2311     size_t purge_goal,
2312     bool delete_secure_cookies) {
2313   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2314 
2315   // 1. Count number of the cookies at `priority`. Includes both secure and
2316   // non-secure cookies.
2317   DeletionCookieLists could_be_deleted;
2318   size_t total_could_be_deleted_for_priority =
2319       CountCookiesAndGenerateListsForPossibleDeletion(
2320           priority, could_be_deleted, cookies, delete_secure_cookies);
2321 
2322   // 2. If we have fewer cookies at this priority than we intend to keep/protect
2323   // then just skip this round entirely.
2324   if (total_could_be_deleted_for_priority <= to_protect) {
2325     return 0u;
2326   }
2327 
2328   // 3. Calculate the number of cookies that could be deleted for this round.
2329   // This number is the lesser of either: The number of cookies that exist at
2330   // this {priority, secureness} tuple, or the number of cookies at this
2331   // priority less the number to protect. We won't exceed the `purge_goal` even
2332   // if this resulting value is larger.
2333   size_t total_deletable = could_be_deleted.host_cookies.size() +
2334                            could_be_deleted.domain_cookies.size();
2335   size_t max_cookies_to_delete_this_round = std::min(
2336       total_deletable, total_could_be_deleted_for_priority - to_protect);
2337 
2338   // 4. Remove domain cookies. As per "Origin-Bound Cookies" behavior, domain
2339   // cookies should always be deleted before host cookies.
2340   size_t removed = 0u;
2341   // At this point we have 3 layers of iterators to consider:
2342   // * The `could_be_deleted` list's iterator, which points to...
2343   // * The `cookies` list's iterator, which points to...
2344   // * The CookieMap's iterator which is used to delete the actual cookie from
2345   // the backend.
2346   // For each cookie deleted all three of these will need to erased, in a bottom
2347   // up approach.
2348   for (auto domain_list_it = could_be_deleted.domain_cookies.begin();
2349        domain_list_it != could_be_deleted.domain_cookies.end() &&
2350        removed < purge_goal && max_cookies_to_delete_this_round > 0;) {
2351     auto cookies_list_it = *domain_list_it;
2352     auto cookie_map_it = *cookies_list_it;
2353     // Delete from the cookie store.
2354     InternalDeleteCookie(cookie_map_it, /*sync_to_store=*/true,
2355                          DELETE_COOKIE_EVICTED_DOMAIN);
2356     // Delete from `cookies`.
2357     cookies->erase(cookies_list_it);
2358     // Delete from `could_be_deleted`.
2359     domain_list_it = could_be_deleted.domain_cookies.erase(domain_list_it);
2360 
2361     max_cookies_to_delete_this_round--;
2362     removed++;
2363   }
2364 
2365   // 5. Remove host cookies
2366   for (auto host_list_it = could_be_deleted.host_cookies.begin();
2367        host_list_it != could_be_deleted.host_cookies.end() &&
2368        removed < purge_goal && max_cookies_to_delete_this_round > 0;) {
2369     auto cookies_list_it = *host_list_it;
2370     auto cookie_map_it = *cookies_list_it;
2371     // Delete from the cookie store.
2372     InternalDeleteCookie(cookie_map_it, /*sync_to_store=*/true,
2373                          DELETE_COOKIE_EVICTED_DOMAIN);
2374     // Delete from `cookies`.
2375     cookies->erase(cookies_list_it);
2376     // Delete from `could_be_deleted`.
2377     host_list_it = could_be_deleted.host_cookies.erase(host_list_it);
2378 
2379     max_cookies_to_delete_this_round--;
2380     removed++;
2381   }
2382   return removed;
2383 }
2384 
GarbageCollectExpired(const Time & current,const CookieMapItPair & itpair,CookieItVector * cookie_its)2385 size_t CookieMonster::GarbageCollectExpired(const Time& current,
2386                                             const CookieMapItPair& itpair,
2387                                             CookieItVector* cookie_its) {
2388   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2389 
2390   int num_deleted = 0;
2391   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2392     auto curit = it;
2393     ++it;
2394 
2395     if (curit->second->IsExpired(current)) {
2396       InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
2397       ++num_deleted;
2398     } else if (cookie_its) {
2399       cookie_its->push_back(curit);
2400     }
2401   }
2402 
2403   return num_deleted;
2404 }
2405 
GarbageCollectExpiredPartitionedCookies(const Time & current,const PartitionedCookieMap::iterator & cookie_partition_it,const CookieMapItPair & itpair,CookieItVector * cookie_its)2406 size_t CookieMonster::GarbageCollectExpiredPartitionedCookies(
2407     const Time& current,
2408     const PartitionedCookieMap::iterator& cookie_partition_it,
2409     const CookieMapItPair& itpair,
2410     CookieItVector* cookie_its) {
2411   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2412 
2413   int num_deleted = 0;
2414   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2415     auto curit = it;
2416     ++it;
2417 
2418     if (curit->second->IsExpired(current)) {
2419       InternalDeletePartitionedCookie(cookie_partition_it, curit, true,
2420                                       DELETE_COOKIE_EXPIRED);
2421       ++num_deleted;
2422     } else if (cookie_its) {
2423       cookie_its->push_back(curit);
2424     }
2425   }
2426 
2427   return num_deleted;
2428 }
2429 
GarbageCollectAllExpiredPartitionedCookies(const Time & current)2430 void CookieMonster::GarbageCollectAllExpiredPartitionedCookies(
2431     const Time& current) {
2432   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2433   for (auto it = partitioned_cookies_.begin();
2434        it != partitioned_cookies_.end();) {
2435     // GarbageCollectExpiredPartitionedCookies calls
2436     // InternalDeletePartitionedCookie which may invalidate
2437     // |cur_cookie_partition_it|.
2438     auto cur_cookie_partition_it = it;
2439     ++it;
2440     GarbageCollectExpiredPartitionedCookies(
2441         current, cur_cookie_partition_it,
2442         CookieMapItPair(cur_cookie_partition_it->second->begin(),
2443                         cur_cookie_partition_it->second->end()),
2444         nullptr /*cookie_its*/);
2445   }
2446 }
2447 
GarbageCollectDeleteRange(const Time & current,DeletionCause cause,CookieItVector::iterator it_begin,CookieItVector::iterator it_end)2448 size_t CookieMonster::GarbageCollectDeleteRange(
2449     const Time& current,
2450     DeletionCause cause,
2451     CookieItVector::iterator it_begin,
2452     CookieItVector::iterator it_end) {
2453   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2454 
2455   for (auto it = it_begin; it != it_end; it++) {
2456     InternalDeleteCookie((*it), true, cause);
2457   }
2458   return it_end - it_begin;
2459 }
2460 
GarbageCollectLeastRecentlyAccessed(const base::Time & current,const base::Time & safe_date,size_t purge_goal,CookieItVector cookie_its,base::Time * earliest_time)2461 size_t CookieMonster::GarbageCollectLeastRecentlyAccessed(
2462     const base::Time& current,
2463     const base::Time& safe_date,
2464     size_t purge_goal,
2465     CookieItVector cookie_its,
2466     base::Time* earliest_time) {
2467   DCHECK_LE(purge_goal, cookie_its.size());
2468   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2469 
2470   // Sorts up to *and including* |cookie_its[purge_goal]| (if it exists), so
2471   // |earliest_time| will be properly assigned even if
2472   // |global_purge_it| == |cookie_its.begin() + purge_goal|.
2473   SortLeastRecentlyAccessed(
2474       cookie_its.begin(), cookie_its.end(),
2475       cookie_its.size() < purge_goal ? purge_goal + 1 : purge_goal);
2476   // Find boundary to cookies older than safe_date.
2477   auto global_purge_it = LowerBoundAccessDate(
2478       cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date);
2479   // Only delete the old cookies and delete non-secure ones first.
2480   size_t num_deleted =
2481       GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL,
2482                                 cookie_its.begin(), global_purge_it);
2483   if (global_purge_it != cookie_its.end())
2484     *earliest_time = (*global_purge_it)->second->LastAccessDate();
2485   return num_deleted;
2486 }
2487 
2488 // A wrapper around registry_controlled_domains::GetDomainAndRegistry
2489 // to make clear we're creating a key for our local map or for the persistent
2490 // store's use. Here and in FindCookiesForRegistryControlledHost() are the only
2491 // two places where we need to conditionalize based on key type.
2492 //
2493 // Note that this key algorithm explicitly ignores the scheme.  This is
2494 // because when we're entering cookies into the map from the backing store,
2495 // we in general won't have the scheme at that point.
2496 // In practical terms, this means that file cookies will be stored
2497 // in the map either by an empty string or by UNC name (and will be
2498 // limited by kMaxCookiesPerHost), and extension cookies will be stored
2499 // based on the single extension id, as the extension id won't have the
2500 // form of a DNS host and hence GetKey() will return it unchanged.
2501 //
2502 // Arguably the right thing to do here is to make the key
2503 // algorithm dependent on the scheme, and make sure that the scheme is
2504 // available everywhere the key must be obtained (specfically at backing
2505 // store load time).  This would require either changing the backing store
2506 // database schema to include the scheme (far more trouble than it's worth), or
2507 // separating out file cookies into their own CookieMonster instance and
2508 // thus restricting each scheme to a single cookie monster (which might
2509 // be worth it, but is still too much trouble to solve what is currently a
2510 // non-problem).
2511 //
2512 // static
GetKey(std::string_view domain)2513 std::string CookieMonster::GetKey(std::string_view domain) {
2514   std::string effective_domain(
2515       registry_controlled_domains::GetDomainAndRegistry(
2516           domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
2517   if (effective_domain.empty())
2518     effective_domain = std::string(domain);
2519 
2520   return cookie_util::CookieDomainAsHost(effective_domain);
2521 }
2522 
HasCookieableScheme(const GURL & url)2523 bool CookieMonster::HasCookieableScheme(const GURL& url) {
2524   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2525 
2526   // Make sure the request is on a cookie-able url scheme.
2527   bool is_cookieable = base::ranges::any_of(
2528       cookieable_schemes_, [&url](const std::string& cookieable_scheme) {
2529         return url.SchemeIs(cookieable_scheme.c_str());
2530       });
2531 
2532   if (!is_cookieable) {
2533     // The scheme didn't match any in our allowed list.
2534     DVLOG(net::cookie_util::kVlogPerCookieMonster)
2535         << "WARNING: Unsupported cookie scheme: " << url.scheme();
2536   }
2537   return is_cookieable;
2538 }
2539 
GetAccessSemanticsForCookie(const CanonicalCookie & cookie) const2540 CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookie(
2541     const CanonicalCookie& cookie) const {
2542   if (cookie_access_delegate())
2543     return cookie_access_delegate()->GetAccessSemantics(cookie);
2544   return CookieAccessSemantics::UNKNOWN;
2545 }
2546 
2547 // Test to see if stats should be recorded, and record them if so.
2548 // The goal here is to get sampling for the average browser-hour of
2549 // activity.  We won't take samples when the web isn't being surfed,
2550 // and when the web is being surfed, we'll take samples about every
2551 // kRecordStatisticsIntervalSeconds.
2552 // last_statistic_record_time_ is initialized to Now() rather than null
2553 // in the constructor so that we won't take statistics right after
2554 // startup, to avoid bias from browsers that are started but not used.
RecordPeriodicStats(const base::Time & current_time)2555 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
2556   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2557 
2558   const base::TimeDelta kRecordStatisticsIntervalTime(
2559       base::Seconds(kRecordStatisticsIntervalSeconds));
2560 
2561   // If we've taken statistics recently, return.
2562   if (current_time - last_statistic_record_time_ <=
2563       kRecordStatisticsIntervalTime) {
2564     return;
2565   }
2566 
2567   if (DoRecordPeriodicStats())
2568     last_statistic_record_time_ = current_time;
2569 }
2570 
DoRecordPeriodicStats()2571 bool CookieMonster::DoRecordPeriodicStats() {
2572   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2573 
2574   SCOPED_UMA_HISTOGRAM_TIMER("Cookie.TimeToRecordPeriodicStats");
2575 
2576   // These values are all bogus if we have only partially loaded the cookies.
2577   if (started_fetching_all_cookies_ && !finished_fetching_all_cookies_)
2578     return false;
2579 
2580   base::UmaHistogramCounts100000("Cookie.Count2", cookies_.size());
2581 
2582   if (cookie_access_delegate()) {
2583     std::vector<SchemefulSite> sites;
2584     for (const auto& entry : cookies_) {
2585       sites.emplace_back(
2586           GURL(base::StrCat({url::kHttpsScheme, "://", entry.first})));
2587     }
2588     for (const auto& [partition_key, cookie_map] : partitioned_cookies_) {
2589       for (const auto& [domain, unused_cookie] : *cookie_map) {
2590         sites.emplace_back(
2591             GURL(base::StrCat({url::kHttpsScheme, "://", domain})));
2592       }
2593     }
2594     std::optional<base::flat_map<SchemefulSite, FirstPartySetEntry>>
2595         maybe_sets = cookie_access_delegate()->FindFirstPartySetEntries(
2596             sites,
2597             base::BindOnce(&CookieMonster::RecordPeriodicFirstPartySetsStats,
2598                            weak_ptr_factory_.GetWeakPtr()));
2599     if (maybe_sets.has_value())
2600       RecordPeriodicFirstPartySetsStats(maybe_sets.value());
2601   }
2602 
2603   // Can be up to kMaxCookies.
2604   UMA_HISTOGRAM_COUNTS_10000("Cookie.NumKeys", num_keys_);
2605 
2606   std::map<std::string, size_t> n_same_site_none_cookies;
2607   size_t n_bytes = 0;
2608   std::map<std::string, size_t> n_bytes_per_key;
2609 
2610   for (const auto& [host_key, host_cookie] : cookies_) {
2611     size_t cookie_n_bytes = NameValueSizeBytes(*host_cookie);
2612     n_bytes += cookie_n_bytes;
2613     n_bytes_per_key[host_key] += cookie_n_bytes;
2614 
2615     if (!host_cookie || !host_cookie->IsEffectivelySameSiteNone())
2616       continue;
2617     n_same_site_none_cookies[host_key]++;
2618   }
2619 
2620   size_t max_n_cookies = 0;
2621   for (const auto& entry : n_same_site_none_cookies) {
2622     max_n_cookies = std::max(max_n_cookies, entry.second);
2623   }
2624   size_t max_n_bytes = 0;
2625   for (const auto& entry : n_bytes_per_key) {
2626     max_n_bytes = std::max(max_n_bytes, entry.second);
2627   }
2628 
2629   // Can be up to 180 cookies, the max per-domain.
2630   base::UmaHistogramCounts1000("Cookie.MaxSameSiteNoneCookiesPerKey",
2631                                max_n_cookies);
2632   base::UmaHistogramCounts100000("Cookie.CookieJarSize", n_bytes >> 10);
2633   base::UmaHistogramCounts100000(
2634       "Cookie.AvgCookieJarSizePerKey",
2635       (n_bytes >> 10) / std::max(num_keys_, static_cast<size_t>(1)));
2636   base::UmaHistogramCounts100000("Cookie.MaxCookieJarSizePerKey",
2637                                  max_n_bytes >> 10);
2638 
2639   // Collect stats for partitioned cookies.
2640   base::UmaHistogramCounts1000("Cookie.PartitionCount",
2641                                partitioned_cookies_.size());
2642   base::UmaHistogramCounts100000("Cookie.PartitionedCookieCount",
2643                                  num_partitioned_cookies_);
2644   base::UmaHistogramCounts100000("Cookie.PartitionedCookieCount.Nonced",
2645                                  num_nonced_partitioned_cookies_);
2646   base::UmaHistogramCounts100000(
2647       "Cookie.PartitionedCookieCount.Unnonced",
2648       num_partitioned_cookies_ - num_nonced_partitioned_cookies_);
2649   base::UmaHistogramCounts100000("Cookie.PartitionedCookieJarSizeKibibytes",
2650                                  num_partitioned_cookies_bytes_ >> 10);
2651   base::UmaHistogramCounts100000(
2652       "Cookie.PartitionedCookieJarSizeKibibytes.Nonced",
2653       num_nonced_partitioned_cookie_bytes_ >> 10);
2654   base::UmaHistogramCounts100000(
2655       "Cookie.PartitionedCookieJarSizeKibibytes.Unnonced",
2656       (num_partitioned_cookies_bytes_ - num_nonced_partitioned_cookie_bytes_) >>
2657           10);
2658 
2659   for (const auto& it : bytes_per_cookie_partition_) {
2660     base::UmaHistogramCounts100000("Cookie.CookiePartitionSizeKibibytes",
2661                                    it.second >> 10);
2662   }
2663 
2664   return true;
2665 }
2666 
RecordPeriodicFirstPartySetsStats(base::flat_map<SchemefulSite,FirstPartySetEntry> sets) const2667 void CookieMonster::RecordPeriodicFirstPartySetsStats(
2668     base::flat_map<SchemefulSite, FirstPartySetEntry> sets) const {
2669   base::flat_map<SchemefulSite, std::set<SchemefulSite>> grouped_by_owner;
2670   for (const auto& [site, entry] : sets) {
2671     grouped_by_owner[entry.primary()].insert(site);
2672   }
2673   for (const auto& set : grouped_by_owner) {
2674     int sample = std::accumulate(
2675         set.second.begin(), set.second.end(), 0,
2676         [this](int acc, const net::SchemefulSite& site) -> int {
2677           DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2678           if (!site.has_registrable_domain_or_host())
2679             return acc;
2680           return acc + cookies_.count(GetKey(site.GetURL().host()));
2681         });
2682     base::UmaHistogramCustomCounts("Cookie.PerFirstPartySetCount", sample, 0,
2683                                    4000, 50);
2684   }
2685 }
2686 
DoCookieCallback(base::OnceClosure callback)2687 void CookieMonster::DoCookieCallback(base::OnceClosure callback) {
2688   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2689 
2690   MarkCookieStoreAsInitialized();
2691   FetchAllCookiesIfNecessary();
2692   seen_global_task_ = true;
2693 
2694   if (!finished_fetching_all_cookies_ && store_.get()) {
2695     tasks_pending_.push_back(std::move(callback));
2696     return;
2697   }
2698 
2699   std::move(callback).Run();
2700 }
2701 
DoCookieCallbackForURL(base::OnceClosure callback,const GURL & url)2702 void CookieMonster::DoCookieCallbackForURL(base::OnceClosure callback,
2703                                            const GURL& url) {
2704   DoCookieCallbackForHostOrDomain(std::move(callback), url.host_piece());
2705 }
2706 
DoCookieCallbackForHostOrDomain(base::OnceClosure callback,std::string_view host_or_domain)2707 void CookieMonster::DoCookieCallbackForHostOrDomain(
2708     base::OnceClosure callback,
2709     std::string_view host_or_domain) {
2710   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2711   MarkCookieStoreAsInitialized();
2712   FetchAllCookiesIfNecessary();
2713 
2714   // If cookies for the requested domain key (eTLD+1) have been loaded from DB
2715   // then run the task, otherwise load from DB.
2716   if (!finished_fetching_all_cookies_ && store_.get()) {
2717     // If a global task has been previously seen, queue the task as a global
2718     // task. Note that the CookieMonster may be in the middle of executing
2719     // the global queue, |tasks_pending_| may be empty, which is why another
2720     // bool is needed.
2721     if (seen_global_task_) {
2722       tasks_pending_.push_back(std::move(callback));
2723       return;
2724     }
2725 
2726     // Checks if the domain key has been loaded.
2727     std::string key = GetKey(host_or_domain);
2728     if (keys_loaded_.find(key) == keys_loaded_.end()) {
2729       auto it = tasks_pending_for_key_.find(key);
2730       if (it == tasks_pending_for_key_.end()) {
2731         store_->LoadCookiesForKey(
2732             key, base::BindOnce(&CookieMonster::OnKeyLoaded,
2733                                 weak_ptr_factory_.GetWeakPtr(), key));
2734         it = tasks_pending_for_key_
2735                  .emplace(key, base::circular_deque<base::OnceClosure>())
2736                  .first;
2737       }
2738       it->second.push_back(std::move(callback));
2739       return;
2740     }
2741   }
2742 
2743   std::move(callback).Run();
2744 }
2745 
2746 CookieMonster::CookieSentToSamePort
IsCookieSentToSamePortThatSetIt(const GURL & destination,int source_port,CookieSourceScheme source_scheme)2747 CookieMonster::IsCookieSentToSamePortThatSetIt(
2748     const GURL& destination,
2749     int source_port,
2750     CookieSourceScheme source_scheme) {
2751   if (source_port == url::PORT_UNSPECIFIED)
2752     return CookieSentToSamePort::kSourcePortUnspecified;
2753 
2754   if (source_port == url::PORT_INVALID)
2755     return CookieSentToSamePort::kInvalid;
2756 
2757   int destination_port = destination.EffectiveIntPort();
2758   if (source_port == destination_port)
2759     return CookieSentToSamePort::kYes;
2760 
2761   const std::string& destination_scheme = destination.scheme();
2762   bool destination_port_is_default =
2763       url::DefaultPortForScheme(destination_scheme.c_str(),
2764                                 destination_scheme.length()) ==
2765       destination_port;
2766 
2767   // Since the source port has to be specified if we got to this point, that
2768   // means this is a newer cookie that therefore has its scheme set as well.
2769   DCHECK(source_scheme != CookieSourceScheme::kUnset);
2770   std::string source_scheme_string =
2771       source_scheme == CookieSourceScheme::kSecure
2772           ? url::kHttpsScheme
2773           : url::kHttpScheme;  // wss/ws have the same default port values as
2774                                // https/http, so it's ok that we use these.
2775 
2776   bool source_port_is_default =
2777       url::DefaultPortForScheme(source_scheme_string.c_str(),
2778                                 source_scheme_string.length()) == source_port;
2779 
2780   if (destination_port_is_default && source_port_is_default)
2781     return CookieSentToSamePort::kNoButDefault;
2782 
2783   return CookieSentToSamePort::kNo;
2784 }
2785 
SiteHasCookieInOtherPartition(const net::SchemefulSite & site,const std::optional<CookiePartitionKey> & partition_key) const2786 std::optional<bool> CookieMonster::SiteHasCookieInOtherPartition(
2787     const net::SchemefulSite& site,
2788     const std::optional<CookiePartitionKey>& partition_key) const {
2789   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2790   // If the partition key is null, it implies the partitioned cookies feature is
2791   // not enabled.
2792   if (!partition_key)
2793     return std::nullopt;
2794 
2795   std::string domain = site.GetURL().host();
2796   if (store_ && !finished_fetching_all_cookies_ &&
2797       !keys_loaded_.count(domain)) {
2798     return std::nullopt;
2799   }
2800 
2801   for (const auto& it : partitioned_cookies_) {
2802     if (it.first == partition_key || CookiePartitionKey::HasNonce(it.first))
2803       continue;
2804     if (it.second->find(domain) != it.second->end()) {
2805       return true;
2806     }
2807   }
2808   return false;
2809 }
2810 
2811 }  // namespace net
2812