• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_helper.h"
6 
7 #include "libcef/browser/thread_util.h"
8 #include "libcef/common/net_service/net_service_util.h"
9 
10 #include "base/bind.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/storage_partition.h"
13 #include "content/public/common/url_constants.h"
14 #include "net/base/load_flags.h"
15 #include "net/cookies/cookie_options.h"
16 #include "net/cookies/cookie_util.h"
17 #include "services/network/cookie_manager.h"
18 #include "services/network/public/cpp/resource_request.h"
19 
20 namespace net_service {
21 namespace cookie_helper {
22 
23 namespace {
24 
25 // Do not keep a reference to the object returned by this method.
GetBrowserContext(const CefBrowserContext::Getter & getter)26 CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
27   CEF_REQUIRE_UIT();
28   DCHECK(!getter.is_null());
29 
30   // Will return nullptr if the BrowserContext has been shut down.
31   return getter.Run();
32 }
33 
34 // Do not keep a reference to the object returned by this method.
GetCookieManager(content::BrowserContext * browser_context)35 network::mojom::CookieManager* GetCookieManager(
36     content::BrowserContext* browser_context) {
37   CEF_REQUIRE_UIT();
38   return browser_context->GetDefaultStoragePartition()
39       ->GetCookieManagerForBrowserProcess();
40 }
41 
GetCookieOptions(const network::ResourceRequest & request)42 net::CookieOptions GetCookieOptions(const network::ResourceRequest& request) {
43   // Match the logic from InterceptionJob::FetchCookies and
44   // ChromeContentBrowserClient::ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel.
45   bool should_treat_as_first_party =
46       request.url.SchemeIsCryptographic() &&
47       request.site_for_cookies.scheme() == content::kChromeUIScheme;
48   bool is_main_frame_navigation =
49       request.trusted_params &&
50       request.trusted_params->isolation_info.request_type() ==
51           net::IsolationInfo::RequestType::kMainFrame;
52 
53   // Match the logic from URLRequestHttpJob::AddCookieHeaderAndStart.
54   net::CookieOptions options;
55   options.set_include_httponly();
56   options.set_same_site_cookie_context(
57       net::cookie_util::ComputeSameSiteContextForRequest(
58           request.method, {request.url}, request.site_for_cookies,
59           request.request_initiator, is_main_frame_navigation,
60           should_treat_as_first_party));
61 
62   return options;
63 }
64 
65 //
66 // LOADING COOKIES.
67 //
68 
ContinueWithLoadedCookies(const AllowCookieCallback & allow_cookie_callback,DoneCookieCallback done_callback,const net::CookieAccessResultList & cookies)69 void ContinueWithLoadedCookies(const AllowCookieCallback& allow_cookie_callback,
70                                DoneCookieCallback done_callback,
71                                const net::CookieAccessResultList& cookies) {
72   CEF_REQUIRE_IOT();
73   net::CookieList allowed_cookies;
74   for (const auto& status : cookies) {
75     bool allow = false;
76     allow_cookie_callback.Run(status.cookie, &allow);
77     if (allow)
78       allowed_cookies.push_back(status.cookie);
79   }
80   std::move(done_callback).Run(cookies.size(), std::move(allowed_cookies));
81 }
82 
GetCookieListCallback(const AllowCookieCallback & allow_cookie_callback,DoneCookieCallback done_callback,const net::CookieAccessResultList & included_cookies,const net::CookieAccessResultList &)83 void GetCookieListCallback(const AllowCookieCallback& allow_cookie_callback,
84                            DoneCookieCallback done_callback,
85                            const net::CookieAccessResultList& included_cookies,
86                            const net::CookieAccessResultList&) {
87   CEF_REQUIRE_UIT();
88   CEF_POST_TASK(CEF_IOT,
89                 base::BindOnce(ContinueWithLoadedCookies, allow_cookie_callback,
90                                std::move(done_callback), included_cookies));
91 }
92 
LoadCookiesOnUIThread(const CefBrowserContext::Getter & browser_context_getter,const GURL & url,const net::CookieOptions & options,net::CookiePartitionKeyCollection cookie_partition_key_collection,const AllowCookieCallback & allow_cookie_callback,DoneCookieCallback done_callback)93 void LoadCookiesOnUIThread(
94     const CefBrowserContext::Getter& browser_context_getter,
95     const GURL& url,
96     const net::CookieOptions& options,
97     net::CookiePartitionKeyCollection cookie_partition_key_collection,
98     const AllowCookieCallback& allow_cookie_callback,
99     DoneCookieCallback done_callback) {
100   auto cef_browser_context = GetBrowserContext(browser_context_getter);
101   auto browser_context =
102       cef_browser_context ? cef_browser_context->AsBrowserContext() : nullptr;
103   if (!browser_context) {
104     GetCookieListCallback(allow_cookie_callback, std::move(done_callback),
105                           net::CookieAccessResultList(),
106                           net::CookieAccessResultList());
107     return;
108   }
109 
110   GetCookieManager(browser_context)
111       ->GetCookieList(
112           url, options, cookie_partition_key_collection,
113           base::BindOnce(GetCookieListCallback, allow_cookie_callback,
114                          std::move(done_callback)));
115 }
116 
117 //
118 // SAVING COOKIES.
119 //
120 
121 struct SaveCookiesProgress {
122   DoneCookieCallback done_callback_;
123   int total_count_;
124   net::CookieList allowed_cookies_;
125   int num_cookie_lines_left_;
126 };
127 
SetCanonicalCookieCallback(SaveCookiesProgress * progress,const net::CanonicalCookie & cookie,net::CookieAccessResult access_result)128 void SetCanonicalCookieCallback(SaveCookiesProgress* progress,
129                                 const net::CanonicalCookie& cookie,
130                                 net::CookieAccessResult access_result) {
131   CEF_REQUIRE_UIT();
132   progress->num_cookie_lines_left_--;
133   if (access_result.status.IsInclude()) {
134     progress->allowed_cookies_.push_back(cookie);
135   }
136 
137   // If all the cookie lines have been handled the request can be continued.
138   if (progress->num_cookie_lines_left_ == 0) {
139     CEF_POST_TASK(CEF_IOT,
140                   base::BindOnce(std::move(progress->done_callback_),
141                                  progress->total_count_,
142                                  std::move(progress->allowed_cookies_)));
143     delete progress;
144   }
145 }
146 
SaveCookiesOnUIThread(const CefBrowserContext::Getter & browser_context_getter,const GURL & url,const net::CookieOptions & options,int total_count,net::CookieList cookies,DoneCookieCallback done_callback)147 void SaveCookiesOnUIThread(
148     const CefBrowserContext::Getter& browser_context_getter,
149     const GURL& url,
150     const net::CookieOptions& options,
151     int total_count,
152     net::CookieList cookies,
153     DoneCookieCallback done_callback) {
154   DCHECK(!cookies.empty());
155 
156   auto cef_browser_context = GetBrowserContext(browser_context_getter);
157   auto browser_context =
158       cef_browser_context ? cef_browser_context->AsBrowserContext() : nullptr;
159   if (!browser_context) {
160     std::move(done_callback).Run(0, net::CookieList());
161     return;
162   }
163 
164   network::mojom::CookieManager* cookie_manager =
165       GetCookieManager(browser_context);
166 
167   // |done_callback| needs to be executed once and only once after the list has
168   // been fully processed. |num_cookie_lines_left_| keeps track of how many
169   // async callbacks are currently pending.
170   auto progress = new SaveCookiesProgress;
171   progress->done_callback_ = std::move(done_callback);
172   progress->total_count_ = total_count;
173 
174   // Make sure to wait for the loop to complete.
175   progress->num_cookie_lines_left_ = 1;
176 
177   for (const auto& cookie : cookies) {
178     progress->num_cookie_lines_left_++;
179     cookie_manager->SetCanonicalCookie(
180         cookie, url, options,
181         base::BindOnce(&SetCanonicalCookieCallback, base::Unretained(progress),
182                        cookie));
183   }
184 
185   SetCanonicalCookieCallback(
186       progress, net::CanonicalCookie(),
187       net::CookieAccessResult(net::CookieInclusionStatus(
188           net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)));
189 }
190 
191 }  // namespace
192 
IsCookieableScheme(const GURL & url,const absl::optional<std::vector<std::string>> & cookieable_schemes)193 bool IsCookieableScheme(
194     const GURL& url,
195     const absl::optional<std::vector<std::string>>& cookieable_schemes) {
196   if (!url.has_scheme())
197     return false;
198 
199   if (cookieable_schemes) {
200     // The client has explicitly registered the full set of schemes that should
201     // be supported.
202     const auto url_scheme = url.scheme_piece();
203     for (auto scheme : *cookieable_schemes) {
204       if (url_scheme == scheme)
205         return true;
206     }
207     return false;
208   }
209 
210   // Schemes that support cookies by default.
211   // This should match CookieMonster::kDefaultCookieableSchemes.
212   return url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS();
213 }
214 
LoadCookies(const CefBrowserContext::Getter & browser_context_getter,const network::ResourceRequest & request,const AllowCookieCallback & allow_cookie_callback,DoneCookieCallback done_callback)215 void LoadCookies(const CefBrowserContext::Getter& browser_context_getter,
216                  const network::ResourceRequest& request,
217                  const AllowCookieCallback& allow_cookie_callback,
218                  DoneCookieCallback done_callback) {
219   CEF_REQUIRE_IOT();
220 
221   if ((request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) ||
222       request.credentials_mode == network::mojom::CredentialsMode::kOmit ||
223       request.url.IsAboutBlank()) {
224     // Continue immediately without loading cookies.
225     std::move(done_callback).Run(0, {});
226     return;
227   }
228 
229   CEF_POST_TASK(
230       CEF_UIT, base::BindOnce(LoadCookiesOnUIThread, browser_context_getter,
231                               request.url, GetCookieOptions(request),
232                               net::CookiePartitionKeyCollection(),
233                               allow_cookie_callback, std::move(done_callback)));
234 }
235 
SaveCookies(const CefBrowserContext::Getter & browser_context_getter,const network::ResourceRequest & request,net::HttpResponseHeaders * headers,const AllowCookieCallback & allow_cookie_callback,DoneCookieCallback done_callback)236 void SaveCookies(const CefBrowserContext::Getter& browser_context_getter,
237                  const network::ResourceRequest& request,
238                  net::HttpResponseHeaders* headers,
239                  const AllowCookieCallback& allow_cookie_callback,
240                  DoneCookieCallback done_callback) {
241   CEF_REQUIRE_IOT();
242 
243   if (request.credentials_mode == network::mojom::CredentialsMode::kOmit ||
244       request.url.IsAboutBlank() || !headers ||
245       !headers->HasHeader(net_service::kHTTPSetCookieHeaderName)) {
246     // Continue immediately without saving cookies.
247     std::move(done_callback).Run(0, {});
248     return;
249   }
250 
251   // Match the logic in
252   // URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete.
253   base::Time response_date;
254   if (!headers->GetDateValue(&response_date))
255     response_date = base::Time();
256 
257   const base::StringPiece name(net_service::kHTTPSetCookieHeaderName);
258   std::string cookie_string;
259   size_t iter = 0;
260   net::CookieList allowed_cookies;
261   int total_count = 0;
262 
263   while (headers->EnumerateHeader(&iter, name, &cookie_string)) {
264     total_count++;
265 
266     net::CookieInclusionStatus returned_status;
267     std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
268         request.url, cookie_string, base::Time::Now(),
269         absl::make_optional(response_date), net::CookiePartitionKey::Todo(),
270         &returned_status);
271     if (!returned_status.IsInclude()) {
272       continue;
273     }
274 
275     bool allow = false;
276     allow_cookie_callback.Run(*cookie, &allow);
277     if (allow)
278       allowed_cookies.push_back(*cookie);
279   }
280 
281   if (!allowed_cookies.empty()) {
282     CEF_POST_TASK(
283         CEF_UIT,
284         base::BindOnce(SaveCookiesOnUIThread, browser_context_getter,
285                        request.url, GetCookieOptions(request), total_count,
286                        std::move(allowed_cookies), std::move(done_callback)));
287 
288   } else {
289     std::move(done_callback).Run(total_count, std::move(allowed_cookies));
290   }
291 }
292 
293 }  // namespace cookie_helper
294 }  // namespace net_service