| // Copyright 2019 The Chromium Authors |
| // 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 <memory> |
| #include <vector> |
| |
| #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 { |
| |
| class AwBrowserContext; |
| |
| // 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* GetDefaultInstance(); |
| |
| // If you want to construct the CookieManager for the default profile, use a |
| // null parent_context, as the default AwBrowserContext does not own its |
| // CookieManager (for legacy reasons). All non-default profile CookieManagers |
| // are owned by an AwBrowserContext - a non-null parent_context. |
| explicit CookieManager(AwBrowserContext* parent_context); |
| ~CookieManager(); |
| |
| 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<network::mojom::CookieManager> cookie_manager_remote); |
| |
| base::android::ScopedJavaLocalRef<jobject> GetJavaCookieManager(); |
| |
| // 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<jobject>& obj, |
| jboolean allow); |
| void SetShouldAcceptCookies(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jboolean accept); |
| jboolean GetShouldAcceptCookies( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| void SetCookie(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jstring>& url, |
| const base::android::JavaParamRef<jstring>& value, |
| const base::android::JavaParamRef<jobject>& java_callback); |
| void SetCookieSync(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jstring>& url, |
| const base::android::JavaParamRef<jstring>& value); |
| |
| base::android::ScopedJavaLocalRef<jstring> GetCookie( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jstring>& url); |
| |
| base::android::ScopedJavaLocalRef<jobjectArray> GetCookieInfo( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jstring>& url); |
| |
| void RemoveAllCookies( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jobject>& java_callback); |
| void RemoveSessionCookies( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| const base::android::JavaParamRef<jobject>& java_callback); |
| void RemoveAllCookiesSync(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| void RemoveSessionCookiesSync( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| void RemoveExpiredCookies(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| void FlushCookieStore(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| jboolean HasCookies(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj); |
| bool GetAllowFileSchemeCookies(); |
| jboolean GetAllowFileSchemeCookies( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& 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<network::mojom::CookieManager> |
| // instances might be able to modify the underlying net::CookieStore before |
| // this call finishes. |
| void SetAllowFileSchemeCookies( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jboolean allow); |
| |
| base::FilePath GetCookieStorePath(); |
| |
| private: |
| // 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<void(base::OnceCallback<void(bool)>)> task); |
| void ExecCookieTaskSync( |
| base::OnceCallback<void(base::OnceCallback<void(int)>)> task); |
| void ExecCookieTaskSync(base::OnceCallback<void(base::OnceClosure)> 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<void(bool)> callback); |
| |
| void SetWorkaroundHttpSecureCookiesAsyncHelper(bool allow, |
| base::OnceClosure complete); |
| |
| void GotCookies(const std::vector<net::CanonicalCookie>& 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<void(bool)> callback); |
| void RemoveAllCookiesHelper(base::OnceCallback<void(bool)> callback); |
| void RemoveCookiesCompleted(base::OnceCallback<void(bool)> callback, |
| uint32_t num_deleted); |
| |
| void FlushCookieStoreAsyncHelper(base::OnceClosure complete); |
| |
| void SetMojoCookieManagerAsync( |
| mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_remote, |
| base::OnceClosure complete); |
| void SwapMojoCookieManagerAsync( |
| mojo::PendingRemote<network::mojom::CookieManager> 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(); |
| |
| // The client hint cache should be cleared if cookies are cleared, but if |
| // cookies are cleared before the browser starts we need a way flag the |
| // need to clear them later. |
| void ClearClientHintsCachedPerOriginMapIfNeeded(); |
| |
| // Returns the AwBrowserContext associated with the same profile as this |
| // CookieManager. For the default profile, the AwBrowserContext is not |
| // guaranteed to be initialized, so it may be null. |
| AwBrowserContext* GetContext() const; |
| // Get the storage path for the profile this CookieManager is associated with. |
| base::FilePath GetContextPath() const; |
| |
| // Java object reference. |
| base::android::ScopedJavaGlobalRef<jobject> java_obj_; |
| |
| // If this is the CookieManager for the default profile this will be null, |
| // otherwise it will point to the non-default AwBrowserContext which owns the |
| // CookieManager. |
| const raw_ptr<AwBrowserContext> parent_context_; |
| |
| bool should_clear_client_hints_cached_per_origin_map_{false}; |
| |
| 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<base::SingleThreadTaskRunner> cookie_store_task_runner_; |
| std::unique_ptr<net::CookieStore> 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<base::OnceClosure> tasks_ GUARDED_BY(task_queue_lock_); |
| |
| // The CookieManager shared with the NetworkContext. |
| mojo::Remote<network::mojom::CookieManager> mojo_cookie_manager_; |
| }; |
| |
| } // namespace android_webview |
| |
| #endif // ANDROID_WEBVIEW_BROWSER_COOKIE_MANAGER_H_ |