• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 #include "net/cookies/cookie_monster_change_dispatcher.h"
6 
7 #include <utility>
8 
9 #include "base/functional/bind.h"
10 #include "base/strings/string_piece.h"
11 #include "base/task/single_thread_task_runner.h"
12 #include "base/task/task_runner.h"
13 #include "net/base/features.h"
14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
15 #include "net/cookies/canonical_cookie.h"
16 #include "net/cookies/cookie_access_delegate.h"
17 #include "net/cookies/cookie_change_dispatcher.h"
18 #include "net/cookies/cookie_constants.h"
19 #include "net/cookies/cookie_monster.h"
20 #include "net/cookies/cookie_util.h"
21 
22 namespace net {
23 
24 namespace {
25 
26 // Special key in GlobalDomainMap for global listeners.
27 constexpr base::StringPiece kGlobalDomainKey = base::StringPiece("\0", 1);
28 
29 //
30 constexpr base::StringPiece kGlobalNameKey = base::StringPiece("\0", 1);
31 
32 }  // anonymous namespace
33 
Subscription(base::WeakPtr<CookieMonsterChangeDispatcher> change_dispatcher,std::string domain_key,std::string name_key,GURL url,CookiePartitionKeyCollection cookie_partition_key_collection,bool same_party_attribute_enabled,net::CookieChangeCallback callback)34 CookieMonsterChangeDispatcher::Subscription::Subscription(
35     base::WeakPtr<CookieMonsterChangeDispatcher> change_dispatcher,
36     std::string domain_key,
37     std::string name_key,
38     GURL url,
39     CookiePartitionKeyCollection cookie_partition_key_collection,
40     bool same_party_attribute_enabled,
41     net::CookieChangeCallback callback)
42     : change_dispatcher_(std::move(change_dispatcher)),
43       domain_key_(std::move(domain_key)),
44       name_key_(std::move(name_key)),
45       url_(std::move(url)),
46       cookie_partition_key_collection_(
47           std::move(cookie_partition_key_collection)),
48       callback_(std::move(callback)),
49       same_party_attribute_enabled_(same_party_attribute_enabled),
50       task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
51   DCHECK(url_.is_valid() || url_.is_empty());
52   DCHECK_EQ(url_.is_empty(), domain_key_ == kGlobalDomainKey);
53 }
54 
~Subscription()55 CookieMonsterChangeDispatcher::Subscription::~Subscription() {
56   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
57 
58   if (change_dispatcher_) {
59     change_dispatcher_->UnlinkSubscription(this);
60   }
61 }
62 
DispatchChange(const CookieChangeInfo & change,const CookieAccessDelegate * cookie_access_delegate)63 void CookieMonsterChangeDispatcher::Subscription::DispatchChange(
64     const CookieChangeInfo& change,
65     const CookieAccessDelegate* cookie_access_delegate) {
66   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
67 
68   const CanonicalCookie& cookie = change.cookie;
69 
70   // The net::CookieOptions are hard-coded for now, but future APIs may set
71   // different options. For example, JavaScript observers will not be allowed to
72   // see HTTP-only changes.
73   if (!url_.is_empty()) {
74     bool delegate_treats_url_as_trustworthy =
75         cookie_access_delegate &&
76         cookie_access_delegate->ShouldTreatUrlAsTrustworthy(url_);
77     CookieOptions options = CookieOptions::MakeAllInclusive();
78     CookieSamePartyStatus same_party_status = cookie_util::GetSamePartyStatus(
79         cookie, options, same_party_attribute_enabled_);
80     if (!cookie
81              .IncludeForRequestURL(
82                  url_, options,
83                  CookieAccessParams{change.access_result.access_semantics,
84                                     delegate_treats_url_as_trustworthy,
85                                     same_party_status})
86              .status.IsInclude()) {
87       return;
88     }
89   }
90 
91   if (!cookie_partition_key_collection_.ContainsAllKeys()) {
92     if (cookie_partition_key_collection_.PartitionKeys().empty()) {
93       if (cookie.IsPartitioned()) {
94         return;
95       }
96     } else {
97       DCHECK_EQ(1u, cookie_partition_key_collection_.PartitionKeys().size());
98       const CookiePartitionKey& key =
99           *cookie_partition_key_collection_.PartitionKeys().begin();
100       if (CookiePartitionKey::HasNonce(key) && !cookie.IsPartitioned()) {
101         return;
102       }
103       if (cookie.IsPartitioned() && key != *cookie.PartitionKey()) {
104         return;
105       }
106     }
107   }
108 
109   // TODO(mmenke, pwnall): Run callbacks synchronously?
110   task_runner_->PostTask(
111       FROM_HERE, base::BindOnce(&Subscription::DoDispatchChange,
112                                 weak_ptr_factory_.GetWeakPtr(), change));
113 }
114 
DoDispatchChange(const CookieChangeInfo & change) const115 void CookieMonsterChangeDispatcher::Subscription::DoDispatchChange(
116     const CookieChangeInfo& change) const {
117   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
118 
119   callback_.Run(change);
120 }
121 
CookieMonsterChangeDispatcher(const CookieMonster * cookie_monster,bool same_party_attribute_enabled)122 CookieMonsterChangeDispatcher::CookieMonsterChangeDispatcher(
123     const CookieMonster* cookie_monster,
124     bool same_party_attribute_enabled)
125     : cookie_monster_(cookie_monster),
126       same_party_attribute_enabled_(same_party_attribute_enabled) {}
127 
~CookieMonsterChangeDispatcher()128 CookieMonsterChangeDispatcher::~CookieMonsterChangeDispatcher() {
129   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
130 }
131 
132 // static
DomainKey(const std::string & domain)133 std::string CookieMonsterChangeDispatcher::DomainKey(
134     const std::string& domain) {
135   std::string domain_key =
136       net::registry_controlled_domains::GetDomainAndRegistry(
137           domain, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
138   DCHECK_NE(domain_key, kGlobalDomainKey);
139   return domain_key;
140 }
141 
142 // static
DomainKey(const GURL & url)143 std::string CookieMonsterChangeDispatcher::DomainKey(const GURL& url) {
144   std::string domain_key =
145       net::registry_controlled_domains::GetDomainAndRegistry(
146           url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
147   DCHECK_NE(domain_key, kGlobalDomainKey);
148   return domain_key;
149 }
150 
151 // static
NameKey(std::string name)152 std::string CookieMonsterChangeDispatcher::NameKey(std::string name) {
153   DCHECK_NE(name, kGlobalNameKey);
154   return name;
155 }
156 
157 std::unique_ptr<CookieChangeSubscription>
AddCallbackForCookie(const GURL & url,const std::string & name,const absl::optional<CookiePartitionKey> & cookie_partition_key,CookieChangeCallback callback)158 CookieMonsterChangeDispatcher::AddCallbackForCookie(
159     const GURL& url,
160     const std::string& name,
161     const absl::optional<CookiePartitionKey>& cookie_partition_key,
162     CookieChangeCallback callback) {
163   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
164 
165   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
166       weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url,
167       CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
168       same_party_attribute_enabled_, std::move(callback));
169 
170   LinkSubscription(subscription.get());
171   return subscription;
172 }
173 
174 std::unique_ptr<CookieChangeSubscription>
AddCallbackForUrl(const GURL & url,const absl::optional<CookiePartitionKey> & cookie_partition_key,CookieChangeCallback callback)175 CookieMonsterChangeDispatcher::AddCallbackForUrl(
176     const GURL& url,
177     const absl::optional<CookiePartitionKey>& cookie_partition_key,
178     CookieChangeCallback callback) {
179   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
180 
181   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
182       weak_ptr_factory_.GetWeakPtr(), DomainKey(url),
183       std::string(kGlobalNameKey), url,
184       CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
185       same_party_attribute_enabled_, std::move(callback));
186 
187   LinkSubscription(subscription.get());
188   return subscription;
189 }
190 
191 std::unique_ptr<CookieChangeSubscription>
AddCallbackForAllChanges(CookieChangeCallback callback)192 CookieMonsterChangeDispatcher::AddCallbackForAllChanges(
193     CookieChangeCallback callback) {
194   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
195 
196   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
197       weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey),
198       std::string(kGlobalNameKey), GURL(""),
199       CookiePartitionKeyCollection::ContainsAll(),
200       same_party_attribute_enabled_, std::move(callback));
201 
202   LinkSubscription(subscription.get());
203   return subscription;
204 }
205 
DispatchChange(const CookieChangeInfo & change,bool notify_global_hooks)206 void CookieMonsterChangeDispatcher::DispatchChange(
207     const CookieChangeInfo& change,
208     bool notify_global_hooks) {
209   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
210 
211   DispatchChangeToDomainKey(change, DomainKey(change.cookie.Domain()));
212   if (notify_global_hooks)
213     DispatchChangeToDomainKey(change, std::string(kGlobalDomainKey));
214 }
215 
DispatchChangeToDomainKey(const CookieChangeInfo & change,const std::string & domain_key)216 void CookieMonsterChangeDispatcher::DispatchChangeToDomainKey(
217     const CookieChangeInfo& change,
218     const std::string& domain_key) {
219   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
220 
221   auto it = cookie_domain_map_.find(domain_key);
222   if (it == cookie_domain_map_.end())
223     return;
224 
225   DispatchChangeToNameKey(change, it->second, NameKey(change.cookie.Name()));
226   DispatchChangeToNameKey(change, it->second, std::string(kGlobalNameKey));
227 }
228 
DispatchChangeToNameKey(const CookieChangeInfo & change,CookieNameMap & cookie_name_map,const std::string & name_key)229 void CookieMonsterChangeDispatcher::DispatchChangeToNameKey(
230     const CookieChangeInfo& change,
231     CookieNameMap& cookie_name_map,
232     const std::string& name_key) {
233   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
234 
235   auto it = cookie_name_map.find(name_key);
236   if (it == cookie_name_map.end())
237     return;
238 
239   SubscriptionList& subscription_list = it->second;
240   for (base::LinkNode<Subscription>* node = subscription_list.head();
241        node != subscription_list.end(); node = node->next()) {
242     node->value()->DispatchChange(change,
243                                   cookie_monster_->cookie_access_delegate());
244   }
245 }
246 
LinkSubscription(Subscription * subscription)247 void CookieMonsterChangeDispatcher::LinkSubscription(
248     Subscription* subscription) {
249   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
250 
251   // The subscript operator creates empty maps if the lookups fail. This is
252   // exactly what this method needs.
253   CookieNameMap& cookie_name_map =
254       cookie_domain_map_[subscription->domain_key()];
255   SubscriptionList& subscription_list =
256       cookie_name_map[subscription->name_key()];
257   subscription_list.Append(subscription);
258 }
259 
UnlinkSubscription(Subscription * subscription)260 void CookieMonsterChangeDispatcher::UnlinkSubscription(
261     Subscription* subscription) {
262   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
263 
264   auto cookie_domain_map_iterator =
265       cookie_domain_map_.find(subscription->domain_key());
266   DCHECK(cookie_domain_map_iterator != cookie_domain_map_.end());
267 
268   CookieNameMap& cookie_name_map = cookie_domain_map_iterator->second;
269   auto cookie_name_map_iterator =
270       cookie_name_map.find(subscription->name_key());
271   DCHECK(cookie_name_map_iterator != cookie_name_map.end());
272 
273   SubscriptionList& subscription_list = cookie_name_map_iterator->second;
274   subscription->RemoveFromList();
275   if (!subscription_list.empty())
276     return;
277 
278   cookie_name_map.erase(cookie_name_map_iterator);
279   if (!cookie_name_map.empty())
280     return;
281 
282   cookie_domain_map_.erase(cookie_domain_map_iterator);
283 }
284 
285 }  // namespace net
286