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