| // Copyright (c) 2011 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. | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <memory> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "android_webview/browser/aw_browser_context.h" | 
 | #include "android_webview/browser/aw_cookie_access_policy.h" | 
 | #include "android_webview/browser/net/init_native_callback.h" | 
 | #include "base/android/jni_string.h" | 
 | #include "base/android/path_utils.h" | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/files/file_path.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/location.h" | 
 | #include "base/logging.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/path_service.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/synchronization/lock.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/threading/sequenced_worker_pool.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "content/public/browser/browser_context.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/cookie_store_factory.h" | 
 | #include "jni/AwCookieManager_jni.h" | 
 | #include "net/cookies/cookie_monster.h" | 
 | #include "net/cookies/cookie_options.h" | 
 | #include "net/cookies/cookie_store.h" | 
 | #include "net/cookies/parsed_cookie.h" | 
 | #include "net/extras/sqlite/cookie_crypto_delegate.h" | 
 | #include "net/url_request/url_request_context.h" | 
 | #include "url/url_constants.h" | 
 |  | 
 | using base::FilePath; | 
 | using base::WaitableEvent; | 
 | using base::android::ConvertJavaStringToUTF8; | 
 | using base::android::ConvertJavaStringToUTF16; | 
 | 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 { | 
 |  | 
 | typedef base::Callback<void(bool)> BoolCallback; | 
 | typedef base::Callback<void(int)> IntCallback; | 
 |  | 
 | // Holds a Java BooleanCookieCallback, knows how to invoke it and turn it | 
 | // into a base callback. | 
 | class BoolCookieCallbackHolder { | 
 |  public: | 
 |   BoolCookieCallbackHolder(JNIEnv* env, jobject callback) { | 
 |     callback_.Reset(env, callback); | 
 |   } | 
 |  | 
 |   void Invoke(bool result) { | 
 |     if (!callback_.is_null()) { | 
 |       JNIEnv* env = base::android::AttachCurrentThread(); | 
 |       Java_AwCookieManager_invokeBooleanCookieCallback(env, callback_, result); | 
 |     } | 
 |   } | 
 |  | 
 |   static BoolCallback ConvertToCallback( | 
 |       std::unique_ptr<BoolCookieCallbackHolder> me) { | 
 |     return base::Bind(&BoolCookieCallbackHolder::Invoke, | 
 |                       base::Owned(me.release())); | 
 |   } | 
 |  | 
 |  private: | 
 |   ScopedJavaGlobalRef<jobject> callback_; | 
 |   DISALLOW_COPY_AND_ASSIGN(BoolCookieCallbackHolder); | 
 | }; | 
 |  | 
 | // Construct a closure which signals a waitable event if and when the closure | 
 | // is called the waitable event must still exist. | 
 | static base::Closure SignalEventClosure(WaitableEvent* completion) { | 
 |   return base::Bind(&WaitableEvent::Signal, base::Unretained(completion)); | 
 | } | 
 |  | 
 | static void DiscardBool(const base::Closure& f, bool b) { | 
 |   f.Run(); | 
 | } | 
 |  | 
 | static BoolCallback BoolCallbackAdapter(const base::Closure& f) { | 
 |   return base::Bind(&DiscardBool, f); | 
 | } | 
 |  | 
 | static void DiscardInt(const base::Closure& f, int i) { | 
 |   f.Run(); | 
 | } | 
 |  | 
 | static IntCallback IntCallbackAdapter(const base::Closure& f) { | 
 |   return base::Bind(&DiscardInt, f); | 
 | } | 
 |  | 
 | // Are cookies allowed for file:// URLs by default? | 
 | const bool kDefaultFileSchemeAllowed = false; | 
 |  | 
 | void GetUserDataDir(FilePath* user_data_dir) { | 
 |   if (!PathService::Get(base::DIR_ANDROID_APP_DATA, user_data_dir)) { | 
 |     NOTREACHED() << "Failed to get app data directory for Android WebView"; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // 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. | 
 | class CookieManager { | 
 |  public: | 
 |   static CookieManager* GetInstance(); | 
 |  | 
 |   // Returns the TaskRunner on which the CookieStore lives. | 
 |   base::SingleThreadTaskRunner* GetCookieStoreTaskRunner(); | 
 |   // Returns the CookieStore, creating it if necessary. This must only be called | 
 |   // on the CookieStore TaskRunner. | 
 |   net::CookieStore* GetCookieStore(); | 
 |  | 
 |   void SetShouldAcceptCookies(bool accept); | 
 |   bool GetShouldAcceptCookies(); | 
 |   void SetCookie(const GURL& host, | 
 |                  const std::string& cookie_value, | 
 |                  std::unique_ptr<BoolCookieCallbackHolder> callback); | 
 |   void SetCookieSync(const GURL& host, const std::string& cookie_value); | 
 |   std::string GetCookie(const GURL& host); | 
 |   void RemoveSessionCookies(std::unique_ptr<BoolCookieCallbackHolder> callback); | 
 |   void RemoveAllCookies(std::unique_ptr<BoolCookieCallbackHolder> callback); | 
 |   void RemoveAllCookiesSync(); | 
 |   void RemoveSessionCookiesSync(); | 
 |   void RemoveExpiredCookies(); | 
 |   void FlushCookieStore(); | 
 |   bool HasCookies(); | 
 |   bool AllowFileSchemeCookies(); | 
 |   void SetAcceptFileSchemeCookies(bool accept); | 
 |  | 
 |  private: | 
 |   friend struct base::LazyInstanceTraitsBase<CookieManager>; | 
 |  | 
 |   CookieManager(); | 
 |   ~CookieManager(); | 
 |  | 
 |   void ExecCookieTaskSync(const base::Callback<void(BoolCallback)>& task); | 
 |   void ExecCookieTaskSync(const base::Callback<void(IntCallback)>& task); | 
 |   void ExecCookieTaskSync(const base::Callback<void(base::Closure)>& task); | 
 |   void ExecCookieTask(const base::Closure& task); | 
 |  | 
 |   void SetCookieHelper(const GURL& host, | 
 |                        const std::string& value, | 
 |                        BoolCallback callback); | 
 |  | 
 |   void GetCookieValueAsyncHelper(const GURL& host, | 
 |                                  std::string* result, | 
 |                                  base::Closure complete); | 
 |   void GetCookieValueCompleted(base::Closure complete, | 
 |                                std::string* result, | 
 |                                const std::string& value); | 
 |  | 
 |   void RemoveSessionCookiesHelper(BoolCallback callback); | 
 |   void RemoveAllCookiesHelper(BoolCallback callback); | 
 |   void RemoveCookiesCompleted(BoolCallback callback, uint32_t num_deleted); | 
 |  | 
 |   void FlushCookieStoreAsyncHelper(base::Closure complete); | 
 |  | 
 |   void HasCookiesAsyncHelper(bool* result, base::Closure complete); | 
 |   void HasCookiesCompleted(base::Closure complete, | 
 |                            bool* result, | 
 |                            const net::CookieList& cookies); | 
 |  | 
 |   // This protects the following two bools, as they're used on multiple threads. | 
 |   base::Lock accept_file_scheme_cookies_lock_; | 
 |   // True if cookies should be allowed for file URLs. Can only be changed prior | 
 |   // to creating the CookieStore. | 
 |   bool accept_file_scheme_cookies_; | 
 |   // True once the cookie store has been created. Just used to track when | 
 |   // |accept_file_scheme_cookies_| can no longer be modified. | 
 |   bool cookie_store_created_; | 
 |  | 
 |   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_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CookieManager); | 
 | }; | 
 |  | 
 | namespace { | 
 | base::LazyInstance<CookieManager>::Leaky g_lazy_instance; | 
 | } | 
 |  | 
 | // static | 
 | CookieManager* CookieManager::GetInstance() { | 
 |   return g_lazy_instance.Pointer(); | 
 | } | 
 |  | 
 | CookieManager::CookieManager() | 
 |     : accept_file_scheme_cookies_(kDefaultFileSchemeAllowed), | 
 |       cookie_store_created_(false), | 
 |       cookie_store_client_thread_("CookieMonsterClient"), | 
 |       cookie_store_backend_thread_("CookieMonsterBackend") { | 
 |   cookie_store_client_thread_.Start(); | 
 |   cookie_store_backend_thread_.Start(); | 
 |   cookie_store_task_runner_ = cookie_store_client_thread_.task_runner(); | 
 | } | 
 |  | 
 | CookieManager::~CookieManager() {} | 
 |  | 
 | // 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( | 
 |     const base::Callback<void(BoolCallback)>& task) { | 
 |   WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                            base::WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   ExecCookieTask( | 
 |       base::Bind(task, BoolCallbackAdapter(SignalEventClosure(&completion)))); | 
 |   base::ThreadRestrictions::ScopedAllowWait wait; | 
 |   completion.Wait(); | 
 | } | 
 |  | 
 | // Ignore an int callback. | 
 | void CookieManager::ExecCookieTaskSync( | 
 |     const base::Callback<void(IntCallback)>& task) { | 
 |   WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                            base::WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   ExecCookieTask( | 
 |       base::Bind(task, IntCallbackAdapter(SignalEventClosure(&completion)))); | 
 |   base::ThreadRestrictions::ScopedAllowWait wait; | 
 |   completion.Wait(); | 
 | } | 
 |  | 
 | // Call the supplied closure when you want to signal that the blocked code can | 
 | // continue. | 
 | void CookieManager::ExecCookieTaskSync( | 
 |     const base::Callback<void(base::Closure)>& task) { | 
 |   WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                            base::WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   ExecCookieTask(base::Bind(task, SignalEventClosure(&completion))); | 
 |   base::ThreadRestrictions::ScopedAllowWait wait; | 
 |   completion.Wait(); | 
 | } | 
 |  | 
 | // Executes the |task| using |cookie_store_task_runner_|. | 
 | void CookieManager::ExecCookieTask(const base::Closure& task) { | 
 |   cookie_store_task_runner_->PostTask(FROM_HERE, task); | 
 | } | 
 |  | 
 | base::SingleThreadTaskRunner* CookieManager::GetCookieStoreTaskRunner() { | 
 |   return cookie_store_task_runner_.get(); | 
 | } | 
 |  | 
 | net::CookieStore* CookieManager::GetCookieStore() { | 
 |   DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence()); | 
 |  | 
 |   if (!cookie_store_) { | 
 |     FilePath user_data_dir; | 
 |     GetUserDataDir(&user_data_dir); | 
 |     FilePath cookie_store_path = | 
 |         user_data_dir.Append(FILE_PATH_LITERAL("Cookies")); | 
 |  | 
 |     content::CookieStoreConfig cookie_config(cookie_store_path, true, true, | 
 |                                              nullptr); | 
 |     cookie_config.client_task_runner = cookie_store_task_runner_; | 
 |     cookie_config.background_task_runner = | 
 |         cookie_store_backend_thread_.task_runner(); | 
 |  | 
 |     { | 
 |       base::AutoLock lock(accept_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::setAcceptFileSchemeCookies' | 
 |       // 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 (accept_file_scheme_cookies_) | 
 |         cookie_config.cookieable_schemes.push_back(url::kFileScheme); | 
 |       cookie_store_created_ = true; | 
 |     } | 
 |  | 
 |     cookie_store_ = content::CreateCookieStore(cookie_config); | 
 |   } | 
 |  | 
 |   return cookie_store_.get(); | 
 | } | 
 |  | 
 | void CookieManager::SetShouldAcceptCookies(bool accept) { | 
 |   AwCookieAccessPolicy::GetInstance()->SetShouldAcceptCookies(accept); | 
 | } | 
 |  | 
 | bool CookieManager::GetShouldAcceptCookies() { | 
 |   return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies(); | 
 | } | 
 |  | 
 | void CookieManager::SetCookie( | 
 |     const GURL& host, | 
 |     const std::string& cookie_value, | 
 |     std::unique_ptr<BoolCookieCallbackHolder> callback_holder) { | 
 |   BoolCallback callback = | 
 |       BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder)); | 
 |   ExecCookieTask(base::Bind(&CookieManager::SetCookieHelper, | 
 |                             base::Unretained(this), host, cookie_value, | 
 |                             callback)); | 
 | } | 
 |  | 
 | void CookieManager::SetCookieSync(const GURL& host, | 
 |                                   const std::string& cookie_value) { | 
 |   ExecCookieTaskSync(base::Bind(&CookieManager::SetCookieHelper, | 
 |                                 base::Unretained(this), host, cookie_value)); | 
 | } | 
 |  | 
 | void CookieManager::SetCookieHelper(const GURL& host, | 
 |                                     const std::string& value, | 
 |                                     const BoolCallback callback) { | 
 |   net::CookieOptions options; | 
 |   options.set_include_httponly(); | 
 |  | 
 |   // Log message for catching strict secure cookies related bugs. | 
 |   // TODO(sgurun) temporary. Add UMA stats to monitor, and remove afterwards. | 
 |   if (host.is_valid() && | 
 |       (!host.has_scheme() || host.SchemeIs(url::kHttpScheme))) { | 
 |     net::ParsedCookie parsed_cookie(value); | 
 |     if (parsed_cookie.IsValid() && parsed_cookie.IsSecure()) { | 
 |       LOG(WARNING) << "Strict Secure Cookie policy does not allow setting a " | 
 |                       "secure cookie for " | 
 |                    << host.spec(); | 
 |       GURL::Replacements replace_host; | 
 |       replace_host.SetSchemeStr("https"); | 
 |       GURL new_host = host.ReplaceComponents(replace_host); | 
 |       GetCookieStore()->SetCookieWithOptionsAsync(new_host, value, options, | 
 |                                                   callback); | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   GetCookieStore()->SetCookieWithOptionsAsync(host, value, options, callback); | 
 | } | 
 |  | 
 | std::string CookieManager::GetCookie(const GURL& host) { | 
 |   std::string cookie_value; | 
 |   ExecCookieTaskSync(base::Bind(&CookieManager::GetCookieValueAsyncHelper, | 
 |                                 base::Unretained(this), host, &cookie_value)); | 
 |   return cookie_value; | 
 | } | 
 |  | 
 | void CookieManager::GetCookieValueAsyncHelper(const GURL& host, | 
 |                                               std::string* result, | 
 |                                               base::Closure complete) { | 
 |   net::CookieOptions options; | 
 |   options.set_include_httponly(); | 
 |  | 
 |   GetCookieStore()->GetCookiesWithOptionsAsync( | 
 |       host, options, | 
 |       base::Bind(&CookieManager::GetCookieValueCompleted, | 
 |                  base::Unretained(this), complete, result)); | 
 | } | 
 |  | 
 | void CookieManager::GetCookieValueCompleted(base::Closure complete, | 
 |                                             std::string* result, | 
 |                                             const std::string& value) { | 
 |   *result = value; | 
 |   complete.Run(); | 
 | } | 
 |  | 
 | void CookieManager::RemoveSessionCookies( | 
 |     std::unique_ptr<BoolCookieCallbackHolder> callback_holder) { | 
 |   BoolCallback callback = | 
 |       BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder)); | 
 |   ExecCookieTask(base::Bind(&CookieManager::RemoveSessionCookiesHelper, | 
 |                             base::Unretained(this), callback)); | 
 | } | 
 |  | 
 | void CookieManager::RemoveSessionCookiesSync() { | 
 |   ExecCookieTaskSync(base::Bind(&CookieManager::RemoveSessionCookiesHelper, | 
 |                                 base::Unretained(this))); | 
 | } | 
 |  | 
 | void CookieManager::RemoveSessionCookiesHelper(BoolCallback callback) { | 
 |   GetCookieStore()->DeleteSessionCookiesAsync( | 
 |       base::Bind(&CookieManager::RemoveCookiesCompleted, base::Unretained(this), | 
 |                  callback)); | 
 | } | 
 |  | 
 | void CookieManager::RemoveCookiesCompleted(BoolCallback callback, | 
 |                                            uint32_t num_deleted) { | 
 |   callback.Run(num_deleted > 0u); | 
 | } | 
 |  | 
 | void CookieManager::RemoveAllCookies( | 
 |     std::unique_ptr<BoolCookieCallbackHolder> callback_holder) { | 
 |   BoolCallback callback = | 
 |       BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder)); | 
 |   ExecCookieTask(base::Bind(&CookieManager::RemoveAllCookiesHelper, | 
 |                             base::Unretained(this), callback)); | 
 | } | 
 |  | 
 | void CookieManager::RemoveAllCookiesSync() { | 
 |   ExecCookieTaskSync(base::Bind(&CookieManager::RemoveAllCookiesHelper, | 
 |                                 base::Unretained(this))); | 
 | } | 
 |  | 
 | void CookieManager::RemoveAllCookiesHelper(const BoolCallback callback) { | 
 |   GetCookieStore()->DeleteAllAsync( | 
 |       base::Bind(&CookieManager::RemoveCookiesCompleted, base::Unretained(this), | 
 |                  callback)); | 
 | } | 
 |  | 
 | void CookieManager::RemoveExpiredCookies() { | 
 |   // HasCookies will call GetAllCookiesAsync, which in turn will force a GC. | 
 |   HasCookies(); | 
 | } | 
 |  | 
 | void CookieManager::FlushCookieStore() { | 
 |   ExecCookieTaskSync(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper, | 
 |                                 base::Unretained(this))); | 
 | } | 
 |  | 
 | void CookieManager::FlushCookieStoreAsyncHelper(base::Closure complete) { | 
 |   GetCookieStore()->FlushStore(complete); | 
 | } | 
 |  | 
 | bool CookieManager::HasCookies() { | 
 |   bool has_cookies; | 
 |   ExecCookieTaskSync(base::Bind(&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::Closure complete) { | 
 |   GetCookieStore()->GetAllCookiesAsync( | 
 |       base::Bind(&CookieManager::HasCookiesCompleted, base::Unretained(this), | 
 |                  complete, result)); | 
 | } | 
 |  | 
 | void CookieManager::HasCookiesCompleted(base::Closure complete, | 
 |                                         bool* result, | 
 |                                         const CookieList& cookies) { | 
 |   *result = cookies.size() != 0; | 
 |   complete.Run(); | 
 | } | 
 |  | 
 | bool CookieManager::AllowFileSchemeCookies() { | 
 |   base::AutoLock lock(accept_file_scheme_cookies_lock_); | 
 |   return accept_file_scheme_cookies_; | 
 | } | 
 |  | 
 | void CookieManager::SetAcceptFileSchemeCookies(bool accept) { | 
 |   base::AutoLock lock(accept_file_scheme_cookies_lock_); | 
 |   // Can only modify this before the cookie store is created. | 
 |   if (!cookie_store_created_) | 
 |     accept_file_scheme_cookies_ = accept; | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_SetShouldAcceptCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     jboolean accept) { | 
 |   CookieManager::GetInstance()->SetShouldAcceptCookies(accept); | 
 | } | 
 |  | 
 | static jboolean JNI_AwCookieManager_GetShouldAcceptCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   return CookieManager::GetInstance()->GetShouldAcceptCookies(); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_SetCookie( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     const JavaParamRef<jstring>& url, | 
 |     const JavaParamRef<jstring>& value, | 
 |     const JavaParamRef<jobject>& java_callback) { | 
 |   GURL host(ConvertJavaStringToUTF16(env, url)); | 
 |   std::string cookie_value(ConvertJavaStringToUTF8(env, value)); | 
 |   std::unique_ptr<BoolCookieCallbackHolder> callback( | 
 |       new BoolCookieCallbackHolder(env, java_callback)); | 
 |   CookieManager::GetInstance()->SetCookie(host, cookie_value, | 
 |                                           std::move(callback)); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_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)); | 
 |  | 
 |   CookieManager::GetInstance()->SetCookieSync(host, cookie_value); | 
 | } | 
 |  | 
 | static ScopedJavaLocalRef<jstring> JNI_AwCookieManager_GetCookie( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     const JavaParamRef<jstring>& url) { | 
 |   GURL host(ConvertJavaStringToUTF16(env, url)); | 
 |  | 
 |   return base::android::ConvertUTF8ToJavaString( | 
 |       env, CookieManager::GetInstance()->GetCookie(host)); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_RemoveSessionCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     const JavaParamRef<jobject>& java_callback) { | 
 |   std::unique_ptr<BoolCookieCallbackHolder> callback( | 
 |       new BoolCookieCallbackHolder(env, java_callback)); | 
 |   CookieManager::GetInstance()->RemoveSessionCookies(std::move(callback)); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_RemoveSessionCookiesSync( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   CookieManager::GetInstance()->RemoveSessionCookiesSync(); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_RemoveAllCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     const JavaParamRef<jobject>& java_callback) { | 
 |   std::unique_ptr<BoolCookieCallbackHolder> callback( | 
 |       new BoolCookieCallbackHolder(env, java_callback)); | 
 |   CookieManager::GetInstance()->RemoveAllCookies(std::move(callback)); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_RemoveAllCookiesSync( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   CookieManager::GetInstance()->RemoveAllCookiesSync(); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_RemoveExpiredCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   CookieManager::GetInstance()->RemoveExpiredCookies(); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_FlushCookieStore( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   CookieManager::GetInstance()->FlushCookieStore(); | 
 | } | 
 |  | 
 | static jboolean JNI_AwCookieManager_HasCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   return CookieManager::GetInstance()->HasCookies(); | 
 | } | 
 |  | 
 | static jboolean JNI_AwCookieManager_AllowFileSchemeCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj) { | 
 |   return CookieManager::GetInstance()->AllowFileSchemeCookies(); | 
 | } | 
 |  | 
 | static void JNI_AwCookieManager_SetAcceptFileSchemeCookies( | 
 |     JNIEnv* env, | 
 |     const JavaParamRef<jobject>& obj, | 
 |     jboolean accept) { | 
 |   return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept); | 
 | } | 
 |  | 
 | // The following two methods are used to avoid a circular project dependency. | 
 | // TODO(mmenke):  This is weird. Maybe there should be a leaky Singleton in | 
 | // browser/net that creates and owns there? | 
 |  | 
 | scoped_refptr<base::SingleThreadTaskRunner> GetCookieStoreTaskRunner() { | 
 |   return CookieManager::GetInstance()->GetCookieStoreTaskRunner(); | 
 | } | 
 |  | 
 | net::CookieStore* GetCookieStore() { | 
 |   return CookieManager::GetInstance()->GetCookieStore(); | 
 | } | 
 |  | 
 | }  // namespace android_webview |