// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that can // be found in the LICENSE file. #include "libcef/browser/net_service/cookie_helper.h" #include "libcef/browser/thread_util.h" #include "libcef/common/net_service/net_service_util.h" #include "base/bind.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" #include "net/base/load_flags.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_util.h" #include "services/network/cookie_manager.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_response.h" namespace net_service { namespace { // Do not keep a reference to the CookieManager returned by this method. network::mojom::CookieManager* GetCookieManager( content::BrowserContext* browser_context) { CEF_REQUIRE_UIT(); return content::BrowserContext::GetDefaultStoragePartition(browser_context) ->GetCookieManagerForBrowserProcess(); } // // LOADING COOKIES. // void ContinueWithLoadedCookies(const AllowCookieCallback& allow_cookie_callback, DoneCookieCallback done_callback, const net::CookieStatusList& cookies) { CEF_REQUIRE_IOT(); net::CookieList allowed_cookies; for (const auto& status : cookies) { bool allow = false; allow_cookie_callback.Run(status.cookie, &allow); if (allow) allowed_cookies.push_back(status.cookie); } std::move(done_callback).Run(cookies.size(), std::move(allowed_cookies)); } void GetCookieListCallback(const AllowCookieCallback& allow_cookie_callback, DoneCookieCallback done_callback, const net::CookieStatusList& included_cookies, const net::CookieStatusList&) { CEF_REQUIRE_UIT(); CEF_POST_TASK(CEF_IOT, base::BindOnce(ContinueWithLoadedCookies, allow_cookie_callback, std::move(done_callback), included_cookies)); } void LoadCookiesOnUIThread(content::BrowserContext* browser_context, const GURL& url, const net::CookieOptions& options, const AllowCookieCallback& allow_cookie_callback, DoneCookieCallback done_callback) { CEF_REQUIRE_UIT(); GetCookieManager(browser_context) ->GetCookieList( url, options, base::BindOnce(GetCookieListCallback, allow_cookie_callback, std::move(done_callback))); } // // SAVING COOKIES. // struct SaveCookiesProgress { DoneCookieCallback done_callback_; int total_count_; net::CookieList allowed_cookies_; int num_cookie_lines_left_; }; void SetCanonicalCookieCallback( SaveCookiesProgress* progress, const net::CanonicalCookie& cookie, net::CanonicalCookie::CookieInclusionStatus status) { CEF_REQUIRE_UIT(); progress->num_cookie_lines_left_--; if (status.IsInclude()) { progress->allowed_cookies_.push_back(cookie); } // If all the cookie lines have been handled the request can be continued. if (progress->num_cookie_lines_left_ == 0) { CEF_POST_TASK(CEF_IOT, base::BindOnce(std::move(progress->done_callback_), progress->total_count_, std::move(progress->allowed_cookies_))); delete progress; } } void SaveCookiesOnUIThread(content::BrowserContext* browser_context, const GURL& url, const net::CookieOptions& options, int total_count, net::CookieList cookies, DoneCookieCallback done_callback) { CEF_REQUIRE_UIT(); DCHECK(!cookies.empty()); network::mojom::CookieManager* cookie_manager = GetCookieManager(browser_context); // |done_callback| needs to be executed once and only once after the list has // been fully processed. |num_cookie_lines_left_| keeps track of how many // async callbacks are currently pending. auto progress = new SaveCookiesProgress; progress->done_callback_ = std::move(done_callback); progress->total_count_ = total_count; // Make sure to wait for the loop to complete. progress->num_cookie_lines_left_ = 1; for (const auto& cookie : cookies) { progress->num_cookie_lines_left_++; cookie_manager->SetCanonicalCookie( cookie, url.scheme(), options, base::BindOnce(&SetCanonicalCookieCallback, base::Unretained(progress), cookie)); } SetCanonicalCookieCallback( progress, net::CanonicalCookie(), net::CanonicalCookie::CookieInclusionStatus( net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)); } } // namespace void LoadCookies(content::BrowserContext* browser_context, const network::ResourceRequest& request, const AllowCookieCallback& allow_cookie_callback, DoneCookieCallback done_callback) { CEF_REQUIRE_IOT(); if ((request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) || request.credentials_mode == network::mojom::CredentialsMode::kOmit) { // Continue immediately without loading cookies. std::move(done_callback).Run(0, {}); return; } // Match the logic in URLRequestHttpJob::AddCookieHeaderAndStart. net::CookieOptions options; options.set_include_httponly(); options.set_same_site_cookie_context( net::cookie_util::ComputeSameSiteContextForRequest( request.method, request.url, request.site_for_cookies, request.request_initiator, request.attach_same_site_cookies)); CEF_POST_TASK( CEF_UIT, base::BindOnce(LoadCookiesOnUIThread, browser_context, request.url, options, allow_cookie_callback, std::move(done_callback))); } void SaveCookies(content::BrowserContext* browser_context, const network::ResourceRequest& request, const network::ResourceResponseHead& head, const AllowCookieCallback& allow_cookie_callback, DoneCookieCallback done_callback) { CEF_REQUIRE_IOT(); if (request.credentials_mode == network::mojom::CredentialsMode::kOmit || !head.headers->HasHeader(net_service::kHTTPSetCookieHeaderName)) { // Continue immediately without saving cookies. std::move(done_callback).Run(0, {}); return; } // Match the logic in // URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete. base::Time response_date; if (!head.headers->GetDateValue(&response_date)) response_date = base::Time(); net::CookieOptions options; options.set_include_httponly(); options.set_same_site_cookie_context( net::cookie_util::ComputeSameSiteContextForRequest( request.method, request.url, request.site_for_cookies, request.request_initiator, request.attach_same_site_cookies)); const base::StringPiece name(net_service::kHTTPSetCookieHeaderName); std::string cookie_string; size_t iter = 0; net::CookieList allowed_cookies; int total_count = 0; while (head.headers->EnumerateHeader(&iter, name, &cookie_string)) { total_count++; net::CanonicalCookie::CookieInclusionStatus returned_status; std::unique_ptr cookie = net::CanonicalCookie::Create( request.url, cookie_string, base::Time::Now(), base::make_optional(response_date), &returned_status); if (!returned_status.IsInclude()) { continue; } bool allow = false; allow_cookie_callback.Run(*cookie, &allow); if (allow) allowed_cookies.push_back(*cookie); } if (!allowed_cookies.empty()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(SaveCookiesOnUIThread, browser_context, request.url, options, total_count, std::move(allowed_cookies), std::move(done_callback))); } else { std::move(done_callback).Run(total_count, std::move(allowed_cookies)); } } } // namespace net_service