1 // Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4
5 #include "libcef/browser/net_service/cookie_manager_impl.h"
6
7 #include "libcef/common/net_service/net_service_util.h"
8 #include "libcef/common/time_util.h"
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/storage_partition.h"
14 #include "services/network/public/mojom/cookie_manager.mojom.h"
15 #include "url/gurl.h"
16
17 using network::mojom::CookieManager;
18
19 namespace {
20
21 // Do not keep a reference to the object returned by this method.
GetBrowserContext(const CefBrowserContext::Getter & getter)22 CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
23 CEF_REQUIRE_UIT();
24 DCHECK(!getter.is_null());
25
26 // Will return nullptr if the BrowserContext has been shut down.
27 return getter.Run();
28 }
29
30 // Do not keep a reference to the object returned by this method.
GetCookieManager(CefBrowserContext * browser_context)31 CookieManager* GetCookieManager(CefBrowserContext* browser_context) {
32 CEF_REQUIRE_UIT();
33 return browser_context->AsBrowserContext()
34 ->GetDefaultStoragePartition()
35 ->GetCookieManagerForBrowserProcess();
36 }
37
38 // Always execute the callback asynchronously.
RunAsyncCompletionOnUIThread(CefRefPtr<CefCompletionCallback> callback)39 void RunAsyncCompletionOnUIThread(CefRefPtr<CefCompletionCallback> callback) {
40 if (!callback.get())
41 return;
42 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefCompletionCallback::OnComplete,
43 callback.get()));
44 }
45
46 // Always execute the callback asynchronously.
SetCookieCallbackImpl(CefRefPtr<CefSetCookieCallback> callback,net::CookieAccessResult access_result)47 void SetCookieCallbackImpl(CefRefPtr<CefSetCookieCallback> callback,
48 net::CookieAccessResult access_result) {
49 if (!callback.get())
50 return;
51 const bool is_include = access_result.status.IsInclude();
52 if (!is_include) {
53 LOG(WARNING) << "SetCookie failed with reason: "
54 << access_result.status.GetDebugString();
55 }
56 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefSetCookieCallback::OnComplete,
57 callback.get(), is_include));
58 }
59
60 // Always execute the callback asynchronously.
DeleteCookiesCallbackImpl(CefRefPtr<CefDeleteCookiesCallback> callback,uint32_t num_deleted)61 void DeleteCookiesCallbackImpl(CefRefPtr<CefDeleteCookiesCallback> callback,
62 uint32_t num_deleted) {
63 if (!callback.get())
64 return;
65 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefDeleteCookiesCallback::OnComplete,
66 callback.get(), num_deleted));
67 }
68
ExecuteVisitor(CefRefPtr<CefCookieVisitor> visitor,const CefBrowserContext::Getter & browser_context_getter,const std::vector<net::CanonicalCookie> & cookies)69 void ExecuteVisitor(CefRefPtr<CefCookieVisitor> visitor,
70 const CefBrowserContext::Getter& browser_context_getter,
71 const std::vector<net::CanonicalCookie>& cookies) {
72 CEF_REQUIRE_UIT();
73
74 auto browser_context = GetBrowserContext(browser_context_getter);
75 if (!browser_context)
76 return;
77
78 auto cookie_manager = GetCookieManager(browser_context);
79
80 int total = cookies.size(), count = 0;
81 for (const auto& cc : cookies) {
82 CefCookie cookie;
83 net_service::MakeCefCookie(cc, cookie);
84
85 bool deleteCookie = false;
86 bool keepLooping = visitor->Visit(cookie, count, total, deleteCookie);
87 if (deleteCookie) {
88 cookie_manager->DeleteCanonicalCookie(
89 cc, CookieManager::DeleteCanonicalCookieCallback());
90 }
91 if (!keepLooping)
92 break;
93 count++;
94 }
95 }
96
97 // Always execute the callback asynchronously.
GetAllCookiesCallbackImpl(CefRefPtr<CefCookieVisitor> visitor,const CefBrowserContext::Getter & browser_context_getter,const net::CookieList & cookies)98 void GetAllCookiesCallbackImpl(
99 CefRefPtr<CefCookieVisitor> visitor,
100 const CefBrowserContext::Getter& browser_context_getter,
101 const net::CookieList& cookies) {
102 CEF_POST_TASK(CEF_UIT, base::BindOnce(&ExecuteVisitor, visitor,
103 browser_context_getter, cookies));
104 }
105
GetCookiesCallbackImpl(CefRefPtr<CefCookieVisitor> visitor,const CefBrowserContext::Getter & browser_context_getter,const net::CookieAccessResultList & include_cookies,const net::CookieAccessResultList &)106 void GetCookiesCallbackImpl(
107 CefRefPtr<CefCookieVisitor> visitor,
108 const CefBrowserContext::Getter& browser_context_getter,
109 const net::CookieAccessResultList& include_cookies,
110 const net::CookieAccessResultList&) {
111 net::CookieList cookies;
112 for (const auto& status : include_cookies) {
113 cookies.push_back(status.cookie);
114 }
115 GetAllCookiesCallbackImpl(visitor, browser_context_getter, cookies);
116 }
117
118 } // namespace
119
CefCookieManagerImpl()120 CefCookieManagerImpl::CefCookieManagerImpl() {}
121
Initialize(CefBrowserContext::Getter browser_context_getter,CefRefPtr<CefCompletionCallback> callback)122 void CefCookieManagerImpl::Initialize(
123 CefBrowserContext::Getter browser_context_getter,
124 CefRefPtr<CefCompletionCallback> callback) {
125 CEF_REQUIRE_UIT();
126 DCHECK(!initialized_);
127 DCHECK(!browser_context_getter.is_null());
128 DCHECK(browser_context_getter_.is_null());
129 browser_context_getter_ = browser_context_getter;
130
131 initialized_ = true;
132 if (!init_callbacks_.empty()) {
133 for (auto& callback : init_callbacks_) {
134 std::move(callback).Run();
135 }
136 init_callbacks_.clear();
137 }
138
139 RunAsyncCompletionOnUIThread(callback);
140 }
141
VisitAllCookies(CefRefPtr<CefCookieVisitor> visitor)142 bool CefCookieManagerImpl::VisitAllCookies(
143 CefRefPtr<CefCookieVisitor> visitor) {
144 if (!visitor.get())
145 return false;
146
147 if (!ValidContext()) {
148 StoreOrTriggerInitCallback(base::BindOnce(
149 base::IgnoreResult(&CefCookieManagerImpl::VisitAllCookiesInternal),
150 this, visitor));
151 return true;
152 }
153
154 return VisitAllCookiesInternal(visitor);
155 }
156
VisitUrlCookies(const CefString & url,bool includeHttpOnly,CefRefPtr<CefCookieVisitor> visitor)157 bool CefCookieManagerImpl::VisitUrlCookies(
158 const CefString& url,
159 bool includeHttpOnly,
160 CefRefPtr<CefCookieVisitor> visitor) {
161 if (!visitor.get())
162 return false;
163
164 GURL gurl = GURL(url.ToString());
165 if (!gurl.is_valid())
166 return false;
167
168 if (!ValidContext()) {
169 StoreOrTriggerInitCallback(base::BindOnce(
170 base::IgnoreResult(&CefCookieManagerImpl::VisitUrlCookiesInternal),
171 this, gurl, includeHttpOnly, visitor));
172 return true;
173 }
174
175 return VisitUrlCookiesInternal(gurl, includeHttpOnly, visitor);
176 }
177
SetCookie(const CefString & url,const CefCookie & cookie,CefRefPtr<CefSetCookieCallback> callback)178 bool CefCookieManagerImpl::SetCookie(const CefString& url,
179 const CefCookie& cookie,
180 CefRefPtr<CefSetCookieCallback> callback) {
181 GURL gurl = GURL(url.ToString());
182 if (!gurl.is_valid())
183 return false;
184
185 if (!ValidContext()) {
186 StoreOrTriggerInitCallback(base::BindOnce(
187 base::IgnoreResult(&CefCookieManagerImpl::SetCookieInternal), this,
188 gurl, cookie, callback));
189 return true;
190 }
191
192 return SetCookieInternal(gurl, cookie, callback);
193 }
194
DeleteCookies(const CefString & url,const CefString & cookie_name,CefRefPtr<CefDeleteCookiesCallback> callback)195 bool CefCookieManagerImpl::DeleteCookies(
196 const CefString& url,
197 const CefString& cookie_name,
198 CefRefPtr<CefDeleteCookiesCallback> callback) {
199 // Empty URLs are allowed but not invalid URLs.
200 GURL gurl = GURL(url.ToString());
201 if (!gurl.is_empty() && !gurl.is_valid())
202 return false;
203
204 if (!ValidContext()) {
205 StoreOrTriggerInitCallback(base::BindOnce(
206 base::IgnoreResult(&CefCookieManagerImpl::DeleteCookiesInternal), this,
207 gurl, cookie_name, callback));
208 return true;
209 }
210
211 return DeleteCookiesInternal(gurl, cookie_name, callback);
212 }
213
FlushStore(CefRefPtr<CefCompletionCallback> callback)214 bool CefCookieManagerImpl::FlushStore(
215 CefRefPtr<CefCompletionCallback> callback) {
216 if (!ValidContext()) {
217 StoreOrTriggerInitCallback(base::BindOnce(
218 base::IgnoreResult(&CefCookieManagerImpl::FlushStoreInternal), this,
219 callback));
220 return true;
221 }
222
223 return FlushStoreInternal(callback);
224 }
225
VisitAllCookiesInternal(CefRefPtr<CefCookieVisitor> visitor)226 bool CefCookieManagerImpl::VisitAllCookiesInternal(
227 CefRefPtr<CefCookieVisitor> visitor) {
228 DCHECK(ValidContext());
229 DCHECK(visitor);
230
231 auto browser_context = GetBrowserContext(browser_context_getter_);
232 if (!browser_context)
233 return false;
234
235 GetCookieManager(browser_context)
236 ->GetAllCookies(base::BindOnce(&GetAllCookiesCallbackImpl, visitor,
237 browser_context_getter_));
238 return true;
239 }
240
VisitUrlCookiesInternal(const GURL & url,bool includeHttpOnly,CefRefPtr<CefCookieVisitor> visitor)241 bool CefCookieManagerImpl::VisitUrlCookiesInternal(
242 const GURL& url,
243 bool includeHttpOnly,
244 CefRefPtr<CefCookieVisitor> visitor) {
245 DCHECK(ValidContext());
246 DCHECK(visitor);
247 DCHECK(url.is_valid());
248
249 net::CookieOptions options;
250 if (includeHttpOnly)
251 options.set_include_httponly();
252 options.set_same_site_cookie_context(
253 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
254
255 auto browser_context = GetBrowserContext(browser_context_getter_);
256 if (!browser_context)
257 return false;
258
259 GetCookieManager(browser_context)
260 ->GetCookieList(url, options, net::CookiePartitionKeyCollection(),
261 base::BindOnce(&GetCookiesCallbackImpl, visitor,
262 browser_context_getter_));
263 return true;
264 }
265
SetCookieInternal(const GURL & url,const CefCookie & cookie,CefRefPtr<CefSetCookieCallback> callback)266 bool CefCookieManagerImpl::SetCookieInternal(
267 const GURL& url,
268 const CefCookie& cookie,
269 CefRefPtr<CefSetCookieCallback> callback) {
270 DCHECK(ValidContext());
271 DCHECK(url.is_valid());
272
273 std::string name = CefString(&cookie.name).ToString();
274 std::string value = CefString(&cookie.value).ToString();
275 std::string domain = CefString(&cookie.domain).ToString();
276 std::string path = CefString(&cookie.path).ToString();
277
278 base::Time expiration_time;
279 if (cookie.has_expires)
280 cef_time_to_basetime(cookie.expires, expiration_time);
281
282 net::CookieSameSite same_site =
283 net_service::MakeCookieSameSite(cookie.same_site);
284 net::CookiePriority priority =
285 net_service::MakeCookiePriority(cookie.priority);
286
287 auto canonical_cookie = net::CanonicalCookie::CreateSanitizedCookie(
288 url, name, value, domain, path,
289 base::Time(), // Creation time.
290 expiration_time,
291 base::Time(), // Last access time.
292 cookie.secure ? true : false, cookie.httponly ? true : false, same_site,
293 priority, /*same_party=*/false, net::CookiePartitionKey::Todo());
294
295 if (!canonical_cookie) {
296 SetCookieCallbackImpl(
297 callback, net::CookieAccessResult(net::CookieInclusionStatus(
298 net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)));
299 return true;
300 }
301
302 net::CookieOptions options;
303 if (cookie.httponly)
304 options.set_include_httponly();
305 options.set_same_site_cookie_context(
306 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
307
308 auto browser_context = GetBrowserContext(browser_context_getter_);
309 if (!browser_context)
310 return false;
311
312 GetCookieManager(browser_context)
313 ->SetCanonicalCookie(*canonical_cookie, url, options,
314 base::BindOnce(SetCookieCallbackImpl, callback));
315 return true;
316 }
317
DeleteCookiesInternal(const GURL & url,const CefString & cookie_name,CefRefPtr<CefDeleteCookiesCallback> callback)318 bool CefCookieManagerImpl::DeleteCookiesInternal(
319 const GURL& url,
320 const CefString& cookie_name,
321 CefRefPtr<CefDeleteCookiesCallback> callback) {
322 DCHECK(ValidContext());
323 DCHECK(url.is_empty() || url.is_valid());
324
325 network::mojom::CookieDeletionFilterPtr deletion_filter =
326 network::mojom::CookieDeletionFilter::New();
327
328 if (url.is_empty()) {
329 // Delete all cookies.
330 } else if (cookie_name.empty()) {
331 // Delete all matching host cookies.
332 deletion_filter->host_name = url.host();
333 } else {
334 // Delete all matching host and domain cookies.
335 deletion_filter->url = url;
336 deletion_filter->cookie_name = cookie_name;
337 }
338
339 auto browser_context = GetBrowserContext(browser_context_getter_);
340 if (!browser_context)
341 return false;
342
343 GetCookieManager(browser_context)
344 ->DeleteCookies(std::move(deletion_filter),
345 base::BindOnce(DeleteCookiesCallbackImpl, callback));
346 return true;
347 }
348
FlushStoreInternal(CefRefPtr<CefCompletionCallback> callback)349 bool CefCookieManagerImpl::FlushStoreInternal(
350 CefRefPtr<CefCompletionCallback> callback) {
351 DCHECK(ValidContext());
352
353 auto browser_context = GetBrowserContext(browser_context_getter_);
354 if (!browser_context)
355 return false;
356
357 GetCookieManager(browser_context)
358 ->FlushCookieStore(
359 base::BindOnce(RunAsyncCompletionOnUIThread, callback));
360 return true;
361 }
362
StoreOrTriggerInitCallback(base::OnceClosure callback)363 void CefCookieManagerImpl::StoreOrTriggerInitCallback(
364 base::OnceClosure callback) {
365 if (!CEF_CURRENTLY_ON_UIT()) {
366 CEF_POST_TASK(
367 CEF_UIT,
368 base::BindOnce(&CefCookieManagerImpl::StoreOrTriggerInitCallback, this,
369 std::move(callback)));
370 return;
371 }
372
373 if (initialized_) {
374 std::move(callback).Run();
375 } else {
376 init_callbacks_.emplace_back(std::move(callback));
377 }
378 }
379
ValidContext() const380 bool CefCookieManagerImpl::ValidContext() const {
381 return CEF_CURRENTLY_ON_UIT() && initialized_;
382 }
383
384 // CefCookieManager methods ----------------------------------------------------
385
386 // static
GetGlobalManager(CefRefPtr<CefCompletionCallback> callback)387 CefRefPtr<CefCookieManager> CefCookieManager::GetGlobalManager(
388 CefRefPtr<CefCompletionCallback> callback) {
389 CefRefPtr<CefRequestContext> context = CefRequestContext::GetGlobalContext();
390 return context ? context->GetCookieManager(callback) : nullptr;
391 }
392