// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef ANDROID_WEBVIEW_BROWSER_COOKIE_MANAGER_H_ #define ANDROID_WEBVIEW_BROWSER_COOKIE_MANAGER_H_ #include #include #include "base/android/jni_array.h" #include "base/android/scoped_java_ref.h" #include "base/containers/circular_deque.h" #include "base/files/file_path.h" #include "base/no_destructor.h" #include "base/thread_annotations.h" #include "base/threading/thread.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/cookie_manager.mojom-forward.h" #include "services/network/public/mojom/cookie_manager.mojom.h" class GURL; namespace base { class SingleThreadTaskRunner; } namespace net { class CookieStore; class CanonicalCookie; } namespace android_webview { // CookieManager creates and owns WebView's CookieStore, in addition to handling // calls into the CookieStore from Java. // // Since Java calls can be made on the IO Thread, and must synchronously return // a result, and the CookieStore API allows it to asynchronously return results, // the CookieStore must be run on its own thread, to prevent deadlock. // // Initialization: // // There are two possible scenarios: 1) The CookieManager is used before the // Network Service is initialized. 2) The CookieManager is not used until after // the Network Service is initialized (during content initialization). // // Case 2) is straightforward: When the // ContentBrowserClient::ConfigureNetworkContextParams was called // AwContentBrowserClient will finally call // CookieManager::SwapMojoCookieManagerAsync by calling // CookieManager::SetMojoCookieManager, setting the |mojo_cookie_manager_| // member of CookieManager (the AW one; it's an unfortunately overloaded term). // // In case 1), the CookieManager creates a provisional CookieStore // |cookie_store_|, which it uses for all operations (because the // network::mojom::CookieManager doesn't exist yet): For every cookie task // it receives, the CookieManager first checks for the presence of a // |mojo_cookie_manager_|, and if it doesn't exist, the CookieManager checks for // the presence of a provisionally-created CookieStore, creating one if it // doesn't exist (in GetCookieStore). Then whichever one it found will handle // the cookie task. // // When it comes time to create the NetworkContext, which comes with a // network::mojom::CookieManager, the provisionally-created CookieStore needs to // transfer its contents (with the results of the pre-content-initialization // cookie tasks) to the newly created network::mojom::CookieManager. It does // this by flushing its contents to disk and then calling the same method, // CookieManager::SwapMojoCookieManagerAsync, which binds the newly created // network::mojom::CookieManager to |mojo_cookie_manager_|. Thereafter, any // cookie tasks will be handled by |mojo_cookie_manager_| because it now exists. // // This works because the newly created network::mojom::CookieManager reads from // the same on-disk backing store that the provisionally-created CookieStore // just flushed its contents to. // // Why is this not a race condition? This was addressed in crbug.com/933461. // If the CookieManager receives cookie tasks while the flush is in progress, // those tasks are added to a task queue, which is not executed until after the // new |mojo_cookie_manager_| has finished being set. The new // |mojo_cookie_manager_| only loads from disk upon receiving a task (*not* upon // creation, importantly; see CookieMonster::FetchAllCookiesIfNecessary, which // is only called if cookie tasks are received), so it will not try to load from // disk until the flush is complete. class CookieManager { public: static CookieManager* GetInstance(); CookieManager(const CookieManager&) = delete; CookieManager& operator=(const CookieManager&) = delete; // Passes a |cookie_manager_remote|, which this will use for CookieManager // APIs going forward. Only called in the Network Service path, with the // intention this is called once during content initialization (when we create // the only NetworkContext). Note: no other cookie tasks will be processed // while this operation is running. void SetMojoCookieManager( mojo::PendingRemote cookie_manager_remote); // Configure whether or not this CookieManager should workaround cookies // specified for insecure URLs with the 'Secure' directive. See // |workaround_http_secure_cookies_| for the default behavior. This should not // be needed in production, as the default is the desirable behavior. void SetWorkaroundHttpSecureCookiesForTesting( JNIEnv* env, const base::android::JavaParamRef& obj, jboolean allow); void SetShouldAcceptCookies(JNIEnv* env, const base::android::JavaParamRef& obj, jboolean accept); jboolean GetShouldAcceptCookies( JNIEnv* env, const base::android::JavaParamRef& obj); void SetCookie(JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& url, const base::android::JavaParamRef& value, const base::android::JavaParamRef& java_callback); void SetCookieSync(JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& url, const base::android::JavaParamRef& value); base::android::ScopedJavaLocalRef GetCookie( JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& url); base::android::ScopedJavaLocalRef GetCookieInfo( JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& url); void RemoveAllCookies( JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& java_callback); void RemoveSessionCookies( JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& java_callback); void RemoveAllCookiesSync(JNIEnv* env, const base::android::JavaParamRef& obj); void RemoveSessionCookiesSync( JNIEnv* env, const base::android::JavaParamRef& obj); void RemoveExpiredCookies(JNIEnv* env, const base::android::JavaParamRef& obj); void FlushCookieStore(JNIEnv* env, const base::android::JavaParamRef& obj); jboolean HasCookies(JNIEnv* env, const base::android::JavaParamRef& obj); bool GetAllowFileSchemeCookies(); jboolean GetAllowFileSchemeCookies( JNIEnv* env, const base::android::JavaParamRef& obj); // Configures whether CookieManager and WebView instances will honor requests // to set cookies for file:// scheme URLs. This method must be called (and // must finish execution) before calling any other WebView APIs which modify // the cookie store (otherwise, this is not guaranteed to succeed). // // This blocks the calling thread until its work is done to achieve this // guarantee (otherwise other mojo::Remote // instances might be able to modify the underlying net::CookieStore before // this call finishes. void SetAllowFileSchemeCookies( JNIEnv* env, const base::android::JavaParamRef& obj, jboolean allow); base::FilePath GetCookieStorePath(); private: friend class base::NoDestructor; CookieManager(); ~CookieManager(); // Returns the CookieStore, creating it if necessary. This must only be called // on the CookieStore TaskRunner. net::CookieStore* GetCookieStore(); // Gets the Network Service CookieManager if it's been passed via // |SetMojoCookieManager|. Otherwise (if Network Service is disabled or // content layer has not yet initialized the NetworkContext), this returns // nullptr (and |GetCookieStore| should be used installed). This must only be // called on the CookieStore TaskRunner. network::mojom::CookieManager* GetMojoCookieManager(); void ExecCookieTaskSync( base::OnceCallback)> task); void ExecCookieTaskSync( base::OnceCallback)> task); void ExecCookieTaskSync(base::OnceCallback task); void ExecCookieTask(base::OnceClosure task); // Runs all queued-up cookie tasks in |tasks_|. void RunPendingCookieTasks(); void SetCookieHelper(const GURL& host, const std::string& value, base::OnceCallback callback); void SetWorkaroundHttpSecureCookiesAsyncHelper(bool allow, base::OnceClosure complete); void GotCookies(const std::vector& cookies); void GetCookieListAsyncHelper(const GURL& host, net::CookieList* result, base::OnceClosure complete); void GetCookieListCompleted( base::OnceClosure complete, net::CookieList* result, const net::CookieAccessResultList& value, const net::CookieAccessResultList& excluded_cookies); void RemoveSessionCookiesHelper(base::OnceCallback callback); void RemoveAllCookiesHelper(base::OnceCallback callback); void RemoveCookiesCompleted(base::OnceCallback callback, uint32_t num_deleted); void FlushCookieStoreAsyncHelper(base::OnceClosure complete); void SetMojoCookieManagerAsync( mojo::PendingRemote cookie_manager_remote, base::OnceClosure complete); void SwapMojoCookieManagerAsync( mojo::PendingRemote cookie_manager_remote, base::OnceClosure complete); void HasCookiesAsyncHelper(bool* result, base::OnceClosure complete); void HasCookiesCompleted(base::OnceClosure complete, bool* result, const net::CookieList& cookies); void SetAllowFileSchemeCookiesAsyncHelper(bool allow, base::OnceClosure complete); // |can_change_schemes| indicates whether or not this call was successful, // indicating whether we may update |allow_file_scheme_cookies_|. void SetAllowFileSchemeCookiesCompleted(base::OnceClosure complete, bool allow, bool can_change_schemes); void MigrateCookieStorePath(); base::FilePath cookie_store_path_; // This protects the following bool, as it's used on multiple threads. base::Lock allow_file_scheme_cookies_lock_; // True if cookies should be allowed for file URLs. Can only be changed prior // to creating the CookieStore. bool allow_file_scheme_cookies_ GUARDED_BY(allow_file_scheme_cookies_lock_); // True once the cookie store has been created. Just used to track when // |allow_file_scheme_cookies_| can no longer be modified. Only accessed on // |cookie_store_task_runner_|. bool cookie_store_created_; // Whether or not to workaround 'Secure' cookies set on insecure URLs. See // MaybeFixUpSchemeForSecureCookieAndGetSameSite. Only accessed on // |cookie_store_task_runner_|. Defaults to false starting for apps targeting // >= R. bool workaround_http_secure_cookies_; base::Thread cookie_store_client_thread_; base::Thread cookie_store_backend_thread_; scoped_refptr cookie_store_task_runner_; std::unique_ptr cookie_store_; // Tracks if we're in the middle of a call to SetMojoCookieManager(). See the // note in SetMojoCookieManager(). Must only be accessed on // |cookie_store_task_runner_|. bool setting_new_mojo_cookie_manager_; // |tasks_| is a queue we manage, to allow us to delay tasks until after // SetMojoCookieManager()'s work is done. This is modified on different // threads, so accesses must be guarded by |task_queue_lock_|. base::Lock task_queue_lock_; base::circular_deque tasks_ GUARDED_BY(task_queue_lock_); // The CookieManager shared with the NetworkContext. mojo::Remote mojo_cookie_manager_; }; } // namespace android_webview #endif // ANDROID_WEBVIEW_BROWSER_COOKIE_MANAGER_H_