| // Copyright 2011 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/browser/cookie_manager.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <optional> |
| #include "android_webview/browser/aw_browser_context.h" |
| #include "android_webview/browser/aw_browser_context_store.h" |
| #include "android_webview/browser/aw_client_hints_controller_delegate.h" |
| #include "android_webview/browser/aw_cookie_access_policy.h" |
| #include "android_webview/browser_jni_headers/AwCookieManager_jni.h" |
| #include "android_webview/common/aw_switches.h" |
| #include "base/android/build_info.h" |
| #include "base/android/callback_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/path_utils.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/command_line.h" |
| #include "base/containers/circular_deque.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/no_destructor.h" |
| #include "base/path_service.h" |
| #include "base/synchronization/lock.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/cookie_store_factory.h" |
| #include "net/cookies/canonical_cookie.h" |
| #include "net/cookies/cookie_constants.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/cookies/cookie_options.h" |
| #include "net/cookies/cookie_store.h" |
| #include "net/cookies/cookie_util.h" |
| #include "net/cookies/parsed_cookie.h" |
| #include "net/url_request/url_request_context.h" |
| #include "services/network/cookie_access_delegate_impl.h" |
| #include "services/network/network_service.h" |
| #include "services/network/public/mojom/cookie_manager.mojom.h" |
| #include "url/url_constants.h" |
| |
| using base::WaitableEvent; |
| using base::android::ConvertJavaStringToUTF16; |
| using base::android::ConvertJavaStringToUTF8; |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaGlobalRef; |
| using base::android::ScopedJavaLocalRef; |
| using content::BrowserThread; |
| using net::CookieList; |
| |
| // In the future, we may instead want to inject an explicit CookieStore |
| // dependency into this object during process initialization to avoid |
| // depending on the URLRequestContext. |
| // See issue http://crbug.com/157683 |
| |
| // On the CookieManager methods without a callback and methods with a callback |
| // when that callback is null can be called from any thread, including threads |
| // without a message loop. Methods with a non-null callback must be called on |
| // a thread with a running message loop. |
| |
| namespace android_webview { |
| |
| namespace { |
| |
| void MaybeRunCookieCallback(base::OnceCallback<void(bool)> callback, |
| const bool& result) { |
| if (callback) |
| std::move(callback).Run(result); |
| } |
| |
| const char kSecureCookieHistogramName[] = "Android.WebView.SecureCookieAction"; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class SecureCookieAction { |
| kInvalidUrl = 0, |
| kAlreadySecureScheme = 1, |
| kInvalidCookie = 2, |
| kNotASecureCookie = 3, |
| kFixedUp = 4, |
| kDisallowedAndroidR = 5, |
| kMaxValue = kDisallowedAndroidR, |
| }; |
| |
| GURL MaybeFixUpSchemeForSecureCookie(const GURL& host, |
| const std::string& value, |
| bool workaround_http_secure_cookies, |
| bool* should_allow_cookie) { |
| net::ParsedCookie parsed_cookie(value); |
| |
| *should_allow_cookie = true; |
| |
| // Log message for catching strict secure cookies related bugs. |
| // TODO(ntfschr): try to remove this, based on UMA stats |
| // (https://crbug.com/933981) |
| if (!host.is_valid()) { |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kInvalidUrl); |
| return host; |
| } |
| if (host.has_scheme() && !host.SchemeIs(url::kHttpScheme)) { |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kAlreadySecureScheme); |
| return host; |
| } |
| if (!parsed_cookie.IsValid()) { |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kInvalidCookie); |
| return host; |
| } |
| if (!parsed_cookie.IsSecure()) { |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kNotASecureCookie); |
| return host; |
| } |
| |
| LOG(ERROR) << "Strict Secure Cookie policy does not allow setting a " |
| "secure cookie for " |
| << host.spec() |
| << " for apps targeting >= R. Please either use the 'https:' " |
| "scheme for this URL or omit the 'Secure' directive in the " |
| "cookie value."; |
| if (!workaround_http_secure_cookies) { |
| // Don't allow setting this cookie if we target >= R. |
| *should_allow_cookie = false; |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kDisallowedAndroidR); |
| return host; |
| } |
| |
| base::UmaHistogramEnumeration(kSecureCookieHistogramName, |
| SecureCookieAction::kFixedUp); |
| GURL::Replacements replace_host; |
| replace_host.SetSchemeStr(url::kHttpsScheme); |
| return host.ReplaceComponents(replace_host); |
| } |
| |
| // Construct a closure which signals a waitable event if and when the closure |
| // is called the waitable event must still exist. |
| static base::OnceClosure SignalEventClosure(WaitableEvent* completion) { |
| return base::BindOnce(&WaitableEvent::Signal, base::Unretained(completion)); |
| } |
| |
| static void DiscardBool(base::OnceClosure f, bool b) { |
| std::move(f).Run(); |
| } |
| |
| static base::OnceCallback<void(bool)> BoolCallbackAdapter(base::OnceClosure f) { |
| return base::BindOnce(&DiscardBool, std::move(f)); |
| } |
| |
| static void DiscardInt(base::OnceClosure f, int i) { |
| std::move(f).Run(); |
| } |
| |
| static base::OnceCallback<void(int)> IntCallbackAdapter(base::OnceClosure f) { |
| return base::BindOnce(&DiscardInt, std::move(f)); |
| } |
| |
| // Are cookies allowed for file:// URLs by default? |
| const bool kDefaultFileSchemeAllowed = false; |
| |
| } // namespace |
| |
| // static |
| CookieManager* CookieManager::GetDefaultInstance() { |
| static base::NoDestructor<CookieManager> instance(nullptr); |
| return instance.get(); |
| } |
| |
| namespace { |
| base::FilePath GetPathInAppDirectory(std::string path) { |
| base::FilePath result; |
| if (!base::PathService::Get(base::DIR_ANDROID_APP_DATA, &result)) { |
| NOTREACHED() << "Failed to get app data directory for Android WebView"; |
| } |
| result = result.Append(FILE_PATH_LITERAL(path)); |
| return result; |
| } |
| } // namespace |
| |
| CookieManager::CookieManager(AwBrowserContext* const parent_context) |
| : parent_context_(parent_context), |
| allow_file_scheme_cookies_(kDefaultFileSchemeAllowed), |
| cookie_store_created_(false), |
| workaround_http_secure_cookies_( |
| base::android::BuildInfo::GetInstance()->target_sdk_version() < |
| base::android::SDK_VERSION_R), |
| cookie_store_client_thread_("CookieMonsterClient"), |
| cookie_store_backend_thread_("CookieMonsterBackend"), |
| setting_new_mojo_cookie_manager_(false) { |
| cookie_store_client_thread_.Start(); |
| cookie_store_backend_thread_.Start(); |
| cookie_store_task_runner_ = cookie_store_client_thread_.task_runner(); |
| cookie_store_path_ = GetContextPath().Append(FILE_PATH_LITERAL("Cookies")); |
| if (!parent_context_) { |
| // Default profile |
| MigrateCookieStorePath(); |
| } |
| } |
| |
| CookieManager::~CookieManager() = default; |
| |
| void CookieManager::MigrateCookieStorePath() { |
| base::FilePath old_cookie_store_path = GetPathInAppDirectory("Cookies"); |
| base::FilePath old_cookie_journal_path = |
| GetPathInAppDirectory("Cookies-journal"); |
| base::FilePath new_cookie_journal_path = |
| GetPathInAppDirectory("Default/Cookies-journal"); |
| |
| if (base::PathExists(old_cookie_store_path)) { |
| base::CreateDirectory(cookie_store_path_.DirName()); |
| base::Move(old_cookie_store_path, cookie_store_path_); |
| base::Move(old_cookie_journal_path, new_cookie_journal_path); |
| } |
| } |
| |
| // Executes the |task| on |cookie_store_task_runner_| and waits for it to |
| // complete before returning. |
| // |
| // To execute a CookieTask synchronously you must arrange for Signal to be |
| // called on the waitable event at some point. You can call the bool or int |
| // versions of ExecCookieTaskSync, these will supply the caller with a dummy |
| // callback which takes an int/bool, throws it away and calls Signal. |
| // Alternatively you can call the version which supplies a Closure in which |
| // case you must call Run on it when you want the unblock the calling code. |
| // |
| // Ignore a bool callback. |
| void CookieManager::ExecCookieTaskSync( |
| base::OnceCallback<void(base::OnceCallback<void(bool)>)> task) { |
| WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| ExecCookieTask(base::BindOnce( |
| std::move(task), BoolCallbackAdapter(SignalEventClosure(&completion)))); |
| |
| // Waiting is necessary when implementing synchronous APIs for the WebView |
| // embedder. |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope wait; |
| completion.Wait(); |
| } |
| |
| // Ignore an int callback. |
| void CookieManager::ExecCookieTaskSync( |
| base::OnceCallback<void(base::OnceCallback<void(int)>)> task) { |
| WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| ExecCookieTask(base::BindOnce( |
| std::move(task), IntCallbackAdapter(SignalEventClosure(&completion)))); |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope wait; |
| completion.Wait(); |
| } |
| |
| // Call the supplied closure when you want to signal that the blocked code can |
| // continue. |
| void CookieManager::ExecCookieTaskSync( |
| base::OnceCallback<void(base::OnceClosure)> task) { |
| WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| ExecCookieTask( |
| base::BindOnce(std::move(task), SignalEventClosure(&completion))); |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope wait; |
| completion.Wait(); |
| } |
| |
| // Executes the |task| using |cookie_store_task_runner_|. |
| void CookieManager::ExecCookieTask(base::OnceClosure task) { |
| base::AutoLock lock(task_queue_lock_); |
| tasks_.push_back(std::move(task)); |
| // Unretained is safe, since android_webview::CookieManager is a singleton we |
| // never destroy (we don't need PostTask to do any memory management). |
| cookie_store_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&CookieManager::RunPendingCookieTasks, |
| base::Unretained(this))); |
| } |
| |
| void CookieManager::RunPendingCookieTasks() { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| // Don't do any cookie tasks if in the middle of setting a mojo CookieManager, |
| // we'll call this method when that operation is finished. |
| if (setting_new_mojo_cookie_manager_) |
| return; |
| |
| // Copy tasks into temp_queue to minimize the amount of time in the critical |
| // section, and to mitigate live-lock issues if these tasks append to the task |
| // queue themselves. |
| base::circular_deque<base::OnceClosure> temp_queue; |
| { |
| base::AutoLock lock(task_queue_lock_); |
| temp_queue.swap(tasks_); |
| } |
| while (!temp_queue.empty()) { |
| std::move(temp_queue.front()).Run(); |
| temp_queue.pop_front(); |
| } |
| } |
| |
| base::FilePath CookieManager::GetCookieStorePath() { |
| return cookie_store_path_; |
| } |
| |
| net::CookieStore* CookieManager::GetCookieStore() { |
| // This should only be called for the default context. |
| CHECK(!parent_context_); |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| |
| if (!cookie_store_) { |
| content::CookieStoreConfig cookie_config( |
| cookie_store_path_, /* restore_old_session_cookies= */ true, |
| /* persist_session_cookies= */ true); |
| cookie_config.client_task_runner = cookie_store_task_runner_; |
| cookie_config.background_task_runner = |
| cookie_store_backend_thread_.task_runner(); |
| |
| { |
| base::AutoLock lock(allow_file_scheme_cookies_lock_); |
| |
| // There are some unknowns about how to correctly handle file:// cookies, |
| // and our implementation for this is not robust. http://crbug.com/582985 |
| // |
| // TODO(mmenke): This call should be removed once we can deprecate and |
| // remove the Android WebView 'CookieManager::SetAllowFileSchemeCookies' |
| // method. Until then, note that this is just not a great idea. |
| cookie_config.cookieable_schemes.insert( |
| cookie_config.cookieable_schemes.begin(), |
| net::CookieMonster::kDefaultCookieableSchemes, |
| net::CookieMonster::kDefaultCookieableSchemes + |
| net::CookieMonster::kDefaultCookieableSchemesCount); |
| if (allow_file_scheme_cookies_) |
| cookie_config.cookieable_schemes.push_back(url::kFileScheme); |
| cookie_store_created_ = true; |
| } |
| |
| cookie_store_ = |
| content::CreateCookieStore(std::move(cookie_config), nullptr); |
| auto cookie_access_delegate_type = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kWebViewEnableModernCookieSameSite) |
| ? network::mojom::CookieAccessDelegateType::ALWAYS_NONLEGACY |
| : network::mojom::CookieAccessDelegateType::ALWAYS_LEGACY; |
| cookie_store_->SetCookieAccessDelegate( |
| std::make_unique<network::CookieAccessDelegateImpl>( |
| cookie_access_delegate_type, nullptr /* first_party_sets */)); |
| } |
| |
| return cookie_store_.get(); |
| } |
| |
| network::mojom::CookieManager* CookieManager::GetMojoCookieManager() { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| if (!mojo_cookie_manager_.is_bound()) |
| return nullptr; |
| return mojo_cookie_manager_.get(); |
| } |
| |
| void CookieManager::SetMojoCookieManager( |
| mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_remote) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::SetMojoCookieManagerAsync, |
| base::Unretained(this), |
| std::move(cookie_manager_remote))); |
| } |
| |
| void CookieManager::SetMojoCookieManagerAsync( |
| mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_remote, |
| base::OnceClosure complete) { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| setting_new_mojo_cookie_manager_ = true; |
| // For simplicity, only permit this method to be called once (otherwise, we |
| // must sometimes flush the mojo_cookie_manager_ instead of cookie_store_). |
| DCHECK(!mojo_cookie_manager_.is_bound()); |
| if (!cookie_store_created_) { |
| SwapMojoCookieManagerAsync(std::move(cookie_manager_remote), |
| std::move(complete)); |
| return; |
| } |
| |
| GetCookieStore()->FlushStore(base::BindOnce( |
| &CookieManager::SwapMojoCookieManagerAsync, base::Unretained(this), |
| std::move(cookie_manager_remote), std::move(complete))); |
| } |
| |
| void CookieManager::SwapMojoCookieManagerAsync( |
| mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_remote, |
| base::OnceClosure complete) { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| mojo_cookie_manager_.Bind(std::move(cookie_manager_remote)); |
| setting_new_mojo_cookie_manager_ = false; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CookieManager::ClearClientHintsCachedPerOriginMapIfNeeded, |
| base::Unretained(this))); |
| std::move(complete).Run(); // unblock content initialization |
| RunPendingCookieTasks(); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| CookieManager::GetJavaCookieManager() { |
| if (!java_obj_) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| java_obj_ = |
| Java_AwCookieManager_create(env, reinterpret_cast<intptr_t>(this)); |
| } |
| return base::android::ScopedJavaLocalRef<jobject>(java_obj_); |
| } |
| |
| void CookieManager::SetWorkaroundHttpSecureCookiesForTesting( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean allow) { |
| ExecCookieTaskSync( |
| base::BindOnce(&CookieManager::SetWorkaroundHttpSecureCookiesAsyncHelper, |
| base::Unretained(this), allow)); |
| } |
| |
| void CookieManager::SetWorkaroundHttpSecureCookiesAsyncHelper( |
| bool allow, |
| base::OnceClosure complete) { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| workaround_http_secure_cookies_ = allow; |
| std::move(complete).Run(); |
| } |
| |
| void CookieManager::SetShouldAcceptCookies(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean accept) { |
| AwCookieAccessPolicy::GetInstance()->SetShouldAcceptCookies(accept); |
| } |
| |
| jboolean CookieManager::GetShouldAcceptCookies( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies(); |
| } |
| |
| void CookieManager::SetCookie(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jstring>& url, |
| const JavaParamRef<jstring>& value, |
| const JavaParamRef<jobject>& java_callback) { |
| DCHECK(java_callback) << "Unexpected null Java callback"; |
| GURL host(ConvertJavaStringToUTF16(env, url)); |
| std::string cookie_value(ConvertJavaStringToUTF8(env, value)); |
| base::OnceCallback<void(bool)> callback = |
| base::BindOnce(&base::android::RunBooleanCallbackAndroid, |
| ScopedJavaGlobalRef<jobject>(java_callback)); |
| |
| ExecCookieTask(base::BindOnce(&CookieManager::SetCookieHelper, |
| base::Unretained(this), host, cookie_value, |
| std::move(callback))); |
| } |
| |
| void CookieManager::SetCookieSync(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jstring>& url, |
| const JavaParamRef<jstring>& value) { |
| GURL host(ConvertJavaStringToUTF16(env, url)); |
| std::string cookie_value(ConvertJavaStringToUTF8(env, value)); |
| |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::SetCookieHelper, |
| base::Unretained(this), host, |
| cookie_value)); |
| } |
| |
| void CookieManager::SetCookieHelper(const GURL& host, |
| const std::string& value, |
| base::OnceCallback<void(bool)> callback) { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| |
| bool should_allow_cookie = true; |
| const GURL& new_host = MaybeFixUpSchemeForSecureCookie( |
| host, value, workaround_http_secure_cookies_, &should_allow_cookie); |
| |
| std::unique_ptr<net::CanonicalCookie> cc(net::CanonicalCookie::Create( |
| new_host, value, base::Time::Now(), std::nullopt /* server_time */, |
| net::CookiePartitionKey::FromWire(net::SchemefulSite(new_host)))); |
| |
| if (!cc || !should_allow_cookie) { |
| MaybeRunCookieCallback(std::move(callback), false); |
| return; |
| } |
| |
| // Note: CookieStore and network::CookieManager both accept a |
| // CookieAccessResult callback. WebView only cares about boolean success, |
| // which is why we use |AdaptCookieAccessResultToBool|. This is temporary |
| // technical debt until we fully launch the Network Service code path. |
| if (GetMojoCookieManager()) { |
| // *cc.get() is safe, because network::CookieManager::SetCanonicalCookie |
| // will make a copy before our smart pointer goes out of scope. |
| GetMojoCookieManager()->SetCanonicalCookie( |
| *cc.get(), new_host, net::CookieOptions::MakeAllInclusive(), |
| base::BindOnce(net::cookie_util::IsCookieAccessResultInclude) |
| .Then(std::move(callback))); |
| } else { |
| GetCookieStore()->SetCanonicalCookieAsync( |
| std::move(cc), new_host, net::CookieOptions::MakeAllInclusive(), |
| base::BindOnce(net::cookie_util::IsCookieAccessResultInclude) |
| .Then(std::move(callback))); |
| } |
| } |
| |
| ScopedJavaLocalRef<jstring> CookieManager::GetCookie( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jstring>& url) { |
| GURL host(ConvertJavaStringToUTF16(env, url)); |
| |
| net::CookieList cookie_list; |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::GetCookieListAsyncHelper, |
| base::Unretained(this), host, |
| &cookie_list)); |
| |
| return base::android::ConvertUTF8ToJavaString( |
| env, net::CanonicalCookie::BuildCookieLine(cookie_list)); |
| } |
| |
| ScopedJavaLocalRef<jobjectArray> CookieManager::GetCookieInfo( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jstring>& url) { |
| GURL host(ConvertJavaStringToUTF16(env, url)); |
| |
| net::CookieList cookie_list; |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::GetCookieListAsyncHelper, |
| base::Unretained(this), host, |
| &cookie_list)); |
| std::vector<std::string> cookie_attributes; |
| for (net::CanonicalCookie cookie : cookie_list) { |
| cookie_attributes.push_back( |
| net::CanonicalCookie::BuildCookieAttributesLine(cookie)); |
| } |
| return base::android::ToJavaArrayOfStrings( |
| env, base::span<const std::string>(cookie_attributes)); |
| } |
| |
| void CookieManager::GetCookieListAsyncHelper(const GURL& host, |
| net::CookieList* result, |
| base::OnceClosure complete) { |
| net::CookieOptions options = net::CookieOptions::MakeAllInclusive(); |
| |
| // TODO(crbug.com/1225444): Complete partitioned cookies implementation for |
| // WebView. The current implementation is a temporary fix for |
| // crbug.com/1442333 to let the app access its 1p partitioned cookie. |
| if (GetMojoCookieManager()) { |
| GetMojoCookieManager()->GetCookieList( |
| host, options, |
| net::CookiePartitionKeyCollection( |
| net::CookiePartitionKey::FromWire(net::SchemefulSite(host))), |
| base::BindOnce(&CookieManager::GetCookieListCompleted, |
| base::Unretained(this), std::move(complete), result)); |
| } else { |
| GetCookieStore()->GetCookieListWithOptionsAsync( |
| host, options, |
| net::CookiePartitionKeyCollection( |
| net::CookiePartitionKey::FromWire(net::SchemefulSite(host))), |
| base::BindOnce(&CookieManager::GetCookieListCompleted, |
| base::Unretained(this), std::move(complete), result)); |
| } |
| } |
| |
| void CookieManager::GetCookieListCompleted( |
| base::OnceClosure complete, |
| net::CookieList* result, |
| const net::CookieAccessResultList& value, |
| const net::CookieAccessResultList& excluded_cookies) { |
| *result = net::cookie_util::StripAccessResults(value); |
| std::move(complete).Run(); |
| } |
| |
| void CookieManager::RemoveSessionCookies( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& java_callback) { |
| DCHECK(java_callback) << "Unexpected null Java callback"; |
| base::OnceCallback<void(bool)> callback = |
| base::BindOnce(&base::android::RunBooleanCallbackAndroid, |
| ScopedJavaGlobalRef<jobject>(java_callback)); |
| |
| ExecCookieTask(base::BindOnce(&CookieManager::RemoveSessionCookiesHelper, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void CookieManager::RemoveSessionCookiesSync(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::RemoveSessionCookiesHelper, |
| base::Unretained(this))); |
| } |
| |
| void CookieManager::RemoveSessionCookiesHelper( |
| base::OnceCallback<void(bool)> callback) { |
| if (GetMojoCookieManager()) { |
| auto match_session_cookies = network::mojom::CookieDeletionFilter::New(); |
| match_session_cookies->session_control = |
| network::mojom::CookieDeletionSessionControl::SESSION_COOKIES; |
| GetMojoCookieManager()->DeleteCookies( |
| std::move(match_session_cookies), |
| base::BindOnce(&CookieManager::RemoveCookiesCompleted, |
| base::Unretained(this), std::move(callback))); |
| } else { |
| GetCookieStore()->DeleteSessionCookiesAsync( |
| base::BindOnce(&CookieManager::RemoveCookiesCompleted, |
| base::Unretained(this), std::move(callback))); |
| } |
| } |
| |
| void CookieManager::RemoveCookiesCompleted( |
| base::OnceCallback<void(bool)> callback, |
| uint32_t num_deleted) { |
| std::move(callback).Run(num_deleted > 0u); |
| } |
| |
| void CookieManager::RemoveAllCookies( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& java_callback) { |
| DCHECK(java_callback) << "Unexpected null Java callback"; |
| |
| base::OnceCallback<void(bool)> callback = |
| base::BindOnce(&base::android::RunBooleanCallbackAndroid, |
| ScopedJavaGlobalRef<jobject>(java_callback)); |
| |
| ExecCookieTask(base::BindOnce(&CookieManager::RemoveAllCookiesHelper, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void CookieManager::RemoveAllCookiesSync(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::RemoveAllCookiesHelper, |
| base::Unretained(this))); |
| } |
| |
| void CookieManager::RemoveAllCookiesHelper( |
| base::OnceCallback<void(bool)> callback) { |
| // Clear client hints preferences when all cookies are cleared. |
| should_clear_client_hints_cached_per_origin_map_ = true; |
| if (GetMojoCookieManager()) { |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &CookieManager::ClearClientHintsCachedPerOriginMapIfNeeded, |
| base::Unretained(this))); |
| // An empty filter matches all cookies. |
| auto match_all_cookies = network::mojom::CookieDeletionFilter::New(); |
| GetMojoCookieManager()->DeleteCookies( |
| std::move(match_all_cookies), |
| base::BindOnce(&CookieManager::RemoveCookiesCompleted, |
| base::Unretained(this), std::move(callback))); |
| } else { |
| // TODO(crbug.com/921655): Support clearing client hints here as well. |
| GetCookieStore()->DeleteAllAsync( |
| base::BindOnce(&CookieManager::RemoveCookiesCompleted, |
| base::Unretained(this), std::move(callback))); |
| } |
| } |
| |
| void CookieManager::RemoveExpiredCookies(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| // HasCookies will call GetAllCookiesAsync, which in turn will force a GC. |
| HasCookies(env, obj); |
| } |
| |
| void CookieManager::FlushCookieStore(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::FlushCookieStoreAsyncHelper, |
| base::Unretained(this))); |
| } |
| |
| void CookieManager::FlushCookieStoreAsyncHelper(base::OnceClosure complete) { |
| if (GetMojoCookieManager()) { |
| GetMojoCookieManager()->FlushCookieStore(std::move(complete)); |
| } else { |
| GetCookieStore()->FlushStore(std::move(complete)); |
| } |
| } |
| |
| jboolean CookieManager::HasCookies(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| bool has_cookies; |
| ExecCookieTaskSync(base::BindOnce(&CookieManager::HasCookiesAsyncHelper, |
| base::Unretained(this), &has_cookies)); |
| return has_cookies; |
| } |
| |
| // TODO(kristianm): Simplify this, copying the entire list around |
| // should not be needed. |
| void CookieManager::HasCookiesAsyncHelper(bool* result, |
| base::OnceClosure complete) { |
| if (GetMojoCookieManager()) { |
| GetMojoCookieManager()->GetAllCookies( |
| base::BindOnce(&CookieManager::HasCookiesCompleted, |
| base::Unretained(this), std::move(complete), result)); |
| } else { |
| GetCookieStore()->GetAllCookiesAsync( |
| base::BindOnce(&CookieManager::HasCookiesCompleted, |
| base::Unretained(this), std::move(complete), result)); |
| } |
| } |
| |
| void CookieManager::HasCookiesCompleted(base::OnceClosure complete, |
| bool* result, |
| const CookieList& cookies) { |
| *result = cookies.size() != 0; |
| std::move(complete).Run(); |
| } |
| |
| bool CookieManager::GetAllowFileSchemeCookies() { |
| base::AutoLock lock(allow_file_scheme_cookies_lock_); |
| return allow_file_scheme_cookies_; |
| } |
| |
| jboolean CookieManager::GetAllowFileSchemeCookies( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return GetAllowFileSchemeCookies(); |
| } |
| |
| void CookieManager::SetAllowFileSchemeCookies(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean allow) { |
| ExecCookieTaskSync( |
| base::BindOnce(&CookieManager::SetAllowFileSchemeCookiesAsyncHelper, |
| base::Unretained(this), allow)); |
| } |
| |
| void CookieManager::SetAllowFileSchemeCookiesAsyncHelper( |
| bool allow, |
| base::OnceClosure complete) { |
| DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); |
| if (GetMojoCookieManager()) { |
| GetMojoCookieManager()->AllowFileSchemeCookies( |
| allow, |
| base::BindOnce(&CookieManager::SetAllowFileSchemeCookiesCompleted, |
| base::Unretained(this), std::move(complete), allow)); |
| } else { |
| // If we have neither a Network Service CookieManager nor have created the |
| // CookieStore, we may modify |allow_file_scheme_cookies_|. |
| bool can_change_schemes = !cookie_store_created_; |
| SetAllowFileSchemeCookiesCompleted(std::move(complete), allow, |
| can_change_schemes); |
| } |
| } |
| |
| void CookieManager::SetAllowFileSchemeCookiesCompleted( |
| base::OnceClosure complete, |
| bool allow, |
| bool can_change_schemes) { |
| // Should only update |allow_file_scheme_cookies_| if |
| // SetAllowFileSchemeCookiesAsyncHelper said this is OK. |
| if (can_change_schemes) { |
| base::AutoLock lock(allow_file_scheme_cookies_lock_); |
| allow_file_scheme_cookies_ = allow; |
| } |
| std::move(complete).Run(); |
| } |
| |
| void CookieManager::ClearClientHintsCachedPerOriginMapIfNeeded() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // If we had a client hint cache clear pending, we should do it as soon as we |
| // next check and see that the browser has been started. |
| if (should_clear_client_hints_cached_per_origin_map_) { |
| GetContext()->GetPrefService()->SetDict( |
| prefs::kClientHintsCachedPerOriginMap, base::Value::Dict()); |
| should_clear_client_hints_cached_per_origin_map_ = false; |
| } |
| } |
| |
| static jlong JNI_AwCookieManager_GetDefaultCookieManager(JNIEnv* env) { |
| return reinterpret_cast<intptr_t>(CookieManager::GetDefaultInstance()); |
| } |
| |
| AwBrowserContext* CookieManager::GetContext() const { |
| if (parent_context_) { |
| return parent_context_; |
| } else { |
| return AwBrowserContext::GetDefault(); |
| } |
| } |
| |
| base::FilePath CookieManager::GetContextPath() const { |
| if (parent_context_) { |
| // Non-default profile |
| return parent_context_->GetPath(); |
| } else { |
| // Default profile |
| return AwBrowserContext::BuildStoragePath( |
| base::FilePath(AwBrowserContextStore::kDefaultContextPath)); |
| } |
| } |
| |
| } // namespace android_webview |