1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "android_webview/native/cookie_manager.h" 6 7 #include "android_webview/browser/aw_browser_context.h" 8 #include "android_webview/browser/aw_cookie_access_policy.h" 9 #include "android_webview/browser/net/init_native_callback.h" 10 #include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h" 11 #include "android_webview/native/aw_browser_dependency_factory.h" 12 #include "base/android/jni_string.h" 13 #include "base/android/path_utils.h" 14 #include "base/bind.h" 15 #include "base/bind_helpers.h" 16 #include "base/file_util.h" 17 #include "base/files/file_path.h" 18 #include "base/lazy_instance.h" 19 #include "base/message_loop/message_loop.h" 20 #include "base/message_loop/message_loop_proxy.h" 21 #include "base/path_service.h" 22 #include "base/synchronization/lock.h" 23 #include "base/synchronization/waitable_event.h" 24 #include "base/threading/sequenced_worker_pool.h" 25 #include "base/threading/thread.h" 26 #include "base/threading/thread_restrictions.h" 27 #include "content/public/browser/browser_context.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/cookie_crypto_delegate.h" 30 #include "content/public/browser/cookie_store_factory.h" 31 #include "content/public/common/url_constants.h" 32 #include "jni/AwCookieManager_jni.h" 33 #include "net/cookies/cookie_monster.h" 34 #include "net/cookies/cookie_options.h" 35 #include "net/url_request/url_request_context.h" 36 37 using base::FilePath; 38 using base::android::ConvertJavaStringToUTF8; 39 using base::android::ConvertJavaStringToUTF16; 40 using content::BrowserThread; 41 using net::CookieList; 42 using net::CookieMonster; 43 44 // In the future, we may instead want to inject an explicit CookieStore 45 // dependency into this object during process initialization to avoid 46 // depending on the URLRequestContext. 47 // See issue http://crbug.com/157683 48 49 // All functions on the CookieManager can be called from any thread, including 50 // threads without a message loop. BrowserThread::IO is used to call methods 51 // on CookieMonster that needs to be called, and called back, on a chrome 52 // thread. 53 54 namespace android_webview { 55 56 namespace { 57 58 // Are cookies allowed for file:// URLs by default? 59 const bool kDefaultFileSchemeAllowed = false; 60 61 void ImportLegacyCookieStore(const FilePath& cookie_store_path) { 62 // We use the old cookie store to create the new cookie store only if the 63 // new cookie store does not exist. 64 if (base::PathExists(cookie_store_path)) 65 return; 66 67 // WebViewClassic gets the database path from Context and appends a 68 // hardcoded name. See: 69 // https://android.googlesource.com/platform/frameworks/base/+/bf6f6f9d/core/java/android/webkit/JniUtil.java 70 // https://android.googlesource.com/platform/external/webkit/+/7151e/ 71 // Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp 72 FilePath old_cookie_store_path; 73 base::android::GetDatabaseDirectory(&old_cookie_store_path); 74 old_cookie_store_path = old_cookie_store_path.Append( 75 FILE_PATH_LITERAL("webviewCookiesChromium.db")); 76 if (base::PathExists(old_cookie_store_path) && 77 !base::Move(old_cookie_store_path, cookie_store_path)) { 78 LOG(WARNING) << "Failed to move old cookie store path from " 79 << old_cookie_store_path.AsUTF8Unsafe() << " to " 80 << cookie_store_path.AsUTF8Unsafe(); 81 } 82 } 83 84 void GetUserDataDir(FilePath* user_data_dir) { 85 if (!PathService::Get(base::DIR_ANDROID_APP_DATA, user_data_dir)) { 86 NOTREACHED() << "Failed to get app data directory for Android WebView"; 87 } 88 } 89 90 class CookieManager { 91 public: 92 static CookieManager* GetInstance(); 93 94 scoped_refptr<net::CookieStore> CreateBrowserThreadCookieStore( 95 AwBrowserContext* browser_context); 96 97 void SetAcceptCookie(bool accept); 98 bool AcceptCookie(); 99 void SetCookie(const GURL& host, const std::string& cookie_value); 100 std::string GetCookie(const GURL& host); 101 void RemoveSessionCookie(); 102 void RemoveAllCookie(); 103 void RemoveExpiredCookie(); 104 void FlushCookieStore(); 105 bool HasCookies(); 106 bool AllowFileSchemeCookies(); 107 void SetAcceptFileSchemeCookies(bool accept); 108 109 private: 110 friend struct base::DefaultLazyInstanceTraits<CookieManager>; 111 112 CookieManager(); 113 ~CookieManager(); 114 115 typedef base::Callback<void(base::WaitableEvent*)> CookieTask; 116 void ExecCookieTask(const CookieTask& task); 117 118 void SetCookieAsyncHelper( 119 const GURL& host, 120 const std::string& value, 121 base::WaitableEvent* completion); 122 void SetCookieCompleted(base::WaitableEvent* completion, bool success); 123 124 void GetCookieValueAsyncHelper( 125 const GURL& host, 126 std::string* result, 127 base::WaitableEvent* completion); 128 void GetCookieValueCompleted(base::WaitableEvent* completion, 129 std::string* result, 130 const std::string& value); 131 132 void RemoveSessionCookieAsyncHelper(base::WaitableEvent* completion); 133 void RemoveAllCookieAsyncHelper(base::WaitableEvent* completion); 134 void RemoveCookiesCompleted(base::WaitableEvent* completion, int num_deleted); 135 136 void FlushCookieStoreAsyncHelper(base::WaitableEvent* completion); 137 138 void HasCookiesAsyncHelper(bool* result, 139 base::WaitableEvent* completion); 140 void HasCookiesCompleted(base::WaitableEvent* completion, 141 bool* result, 142 const CookieList& cookies); 143 144 void CreateCookieMonster( 145 const FilePath& user_data_dir, 146 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, 147 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner); 148 void EnsureCookieMonsterExistsLocked(); 149 bool AllowFileSchemeCookiesLocked(); 150 void SetAcceptFileSchemeCookiesLocked(bool accept); 151 152 scoped_refptr<net::CookieMonster> cookie_monster_; 153 scoped_refptr<base::MessageLoopProxy> cookie_monster_proxy_; 154 base::Lock cookie_monster_lock_; 155 156 // Both these threads are normally NULL. They only exist if CookieManager was 157 // accessed before Chromium was started. 158 scoped_ptr<base::Thread> cookie_monster_client_thread_; 159 scoped_ptr<base::Thread> cookie_monster_backend_thread_; 160 161 DISALLOW_COPY_AND_ASSIGN(CookieManager); 162 }; 163 164 base::LazyInstance<CookieManager>::Leaky g_lazy_instance; 165 166 // static 167 CookieManager* CookieManager::GetInstance() { 168 return g_lazy_instance.Pointer(); 169 } 170 171 CookieManager::CookieManager() { 172 } 173 174 CookieManager::~CookieManager() { 175 } 176 177 void CookieManager::CreateCookieMonster( 178 const FilePath& user_data_dir, 179 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, 180 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) { 181 FilePath cookie_store_path = 182 user_data_dir.Append(FILE_PATH_LITERAL("Cookies")); 183 184 background_task_runner->PostTask( 185 FROM_HERE, 186 base::Bind(ImportLegacyCookieStore, cookie_store_path)); 187 188 net::CookieStore* cookie_store = content::CreatePersistentCookieStore( 189 cookie_store_path, 190 true, 191 NULL, 192 NULL, 193 client_task_runner, 194 background_task_runner, 195 scoped_ptr<content::CookieCryptoDelegate>()); 196 cookie_monster_ = cookie_store->GetCookieMonster(); 197 cookie_monster_->SetPersistSessionCookies(true); 198 SetAcceptFileSchemeCookiesLocked(kDefaultFileSchemeAllowed); 199 } 200 201 void CookieManager::EnsureCookieMonsterExistsLocked() { 202 cookie_monster_lock_.AssertAcquired(); 203 if (cookie_monster_.get()) { 204 return; 205 } 206 207 // Create cookie monster using WebView-specific threads, as the rest of the 208 // browser has not been started yet. 209 FilePath user_data_dir; 210 GetUserDataDir(&user_data_dir); 211 cookie_monster_client_thread_.reset( 212 new base::Thread("CookieMonsterClient")); 213 cookie_monster_client_thread_->Start(); 214 cookie_monster_proxy_ = cookie_monster_client_thread_->message_loop_proxy(); 215 cookie_monster_backend_thread_.reset( 216 new base::Thread("CookieMonsterBackend")); 217 cookie_monster_backend_thread_->Start(); 218 219 CreateCookieMonster(user_data_dir, 220 cookie_monster_proxy_, 221 cookie_monster_backend_thread_->message_loop_proxy()); 222 } 223 224 // Executes the |task| on the |cookie_monster_proxy_| message loop. 225 void CookieManager::ExecCookieTask(const CookieTask& task) { 226 base::WaitableEvent completion(false, false); 227 base::AutoLock lock(cookie_monster_lock_); 228 229 EnsureCookieMonsterExistsLocked(); 230 231 cookie_monster_proxy_->PostTask(FROM_HERE, base::Bind(task, &completion)); 232 233 // We always wait for the posted task to complete, even when it doesn't return 234 // a value, because previous versions of the CookieManager API were 235 // synchronous in most/all cases and the caller may be relying on this. 236 ScopedAllowWaitForLegacyWebViewApi wait; 237 completion.Wait(); 238 } 239 240 scoped_refptr<net::CookieStore> CookieManager::CreateBrowserThreadCookieStore( 241 AwBrowserContext* browser_context) { 242 base::AutoLock lock(cookie_monster_lock_); 243 244 if (cookie_monster_client_thread_) { 245 // We created a cookie monster already on its own threads; we'll just keep 246 // using it rather than creating one on the normal Chromium threads. 247 // CookieMonster is threadsafe, so this is fine. 248 return cookie_monster_; 249 } 250 251 // Go ahead and create the cookie monster using the normal Chromium threads. 252 DCHECK(!cookie_monster_.get()); 253 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO)); 254 255 FilePath user_data_dir; 256 GetUserDataDir(&user_data_dir); 257 DCHECK(browser_context->GetPath() == user_data_dir); 258 259 cookie_monster_proxy_ = 260 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 261 scoped_refptr<base::SequencedTaskRunner> background_task_runner = 262 BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( 263 BrowserThread::GetBlockingPool()->GetSequenceToken()); 264 CreateCookieMonster(user_data_dir, 265 cookie_monster_proxy_, 266 background_task_runner); 267 return cookie_monster_; 268 } 269 270 void CookieManager::SetAcceptCookie(bool accept) { 271 AwCookieAccessPolicy::GetInstance()->SetGlobalAllowAccess(accept); 272 } 273 274 bool CookieManager::AcceptCookie() { 275 return AwCookieAccessPolicy::GetInstance()->GetGlobalAllowAccess(); 276 } 277 278 void CookieManager::SetCookie(const GURL& host, 279 const std::string& cookie_value) { 280 ExecCookieTask(base::Bind(&CookieManager::SetCookieAsyncHelper, 281 base::Unretained(this), 282 host, 283 cookie_value)); 284 } 285 286 void CookieManager::SetCookieAsyncHelper( 287 const GURL& host, 288 const std::string& value, 289 base::WaitableEvent* completion) { 290 net::CookieOptions options; 291 options.set_include_httponly(); 292 293 cookie_monster_->SetCookieWithOptionsAsync( 294 host, value, options, 295 base::Bind(&CookieManager::SetCookieCompleted, 296 base::Unretained(this), 297 completion)); 298 } 299 300 void CookieManager::SetCookieCompleted(base::WaitableEvent* completion, 301 bool success) { 302 // The CookieManager API does not return a value for SetCookie, 303 // so we don't need to propagate the |success| value back to the caller. 304 completion->Signal(); 305 } 306 307 std::string CookieManager::GetCookie(const GURL& host) { 308 std::string cookie_value; 309 ExecCookieTask(base::Bind(&CookieManager::GetCookieValueAsyncHelper, 310 base::Unretained(this), 311 host, 312 &cookie_value)); 313 314 return cookie_value; 315 } 316 317 void CookieManager::GetCookieValueAsyncHelper( 318 const GURL& host, 319 std::string* result, 320 base::WaitableEvent* completion) { 321 net::CookieOptions options; 322 options.set_include_httponly(); 323 324 cookie_monster_->GetCookiesWithOptionsAsync( 325 host, 326 options, 327 base::Bind(&CookieManager::GetCookieValueCompleted, 328 base::Unretained(this), 329 completion, 330 result)); 331 } 332 333 void CookieManager::GetCookieValueCompleted(base::WaitableEvent* completion, 334 std::string* result, 335 const std::string& value) { 336 *result = value; 337 completion->Signal(); 338 } 339 340 void CookieManager::RemoveSessionCookie() { 341 ExecCookieTask(base::Bind(&CookieManager::RemoveSessionCookieAsyncHelper, 342 base::Unretained(this))); 343 } 344 345 void CookieManager::RemoveSessionCookieAsyncHelper( 346 base::WaitableEvent* completion) { 347 cookie_monster_->DeleteSessionCookiesAsync( 348 base::Bind(&CookieManager::RemoveCookiesCompleted, 349 base::Unretained(this), 350 completion)); 351 } 352 353 void CookieManager::RemoveCookiesCompleted(base::WaitableEvent* completion, 354 int num_deleted) { 355 // The CookieManager API does not return a value for removeSessionCookie or 356 // removeAllCookie, so we don't need to propagate the |num_deleted| value back 357 // to the caller. 358 completion->Signal(); 359 } 360 361 void CookieManager::RemoveAllCookie() { 362 ExecCookieTask(base::Bind(&CookieManager::RemoveAllCookieAsyncHelper, 363 base::Unretained(this))); 364 } 365 366 void CookieManager::RemoveAllCookieAsyncHelper( 367 base::WaitableEvent* completion) { 368 cookie_monster_->DeleteAllAsync( 369 base::Bind(&CookieManager::RemoveCookiesCompleted, 370 base::Unretained(this), 371 completion)); 372 } 373 374 void CookieManager::RemoveExpiredCookie() { 375 // HasCookies will call GetAllCookiesAsync, which in turn will force a GC. 376 HasCookies(); 377 } 378 379 void CookieManager::FlushCookieStoreAsyncHelper( 380 base::WaitableEvent* completion) { 381 cookie_monster_->FlushStore(base::Bind(&base::WaitableEvent::Signal, 382 base::Unretained(completion))); 383 } 384 385 void CookieManager::FlushCookieStore() { 386 ExecCookieTask(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper, 387 base::Unretained(this))); 388 } 389 390 bool CookieManager::HasCookies() { 391 bool has_cookies; 392 ExecCookieTask(base::Bind(&CookieManager::HasCookiesAsyncHelper, 393 base::Unretained(this), 394 &has_cookies)); 395 return has_cookies; 396 } 397 398 // TODO(kristianm): Simplify this, copying the entire list around 399 // should not be needed. 400 void CookieManager::HasCookiesAsyncHelper(bool* result, 401 base::WaitableEvent* completion) { 402 cookie_monster_->GetAllCookiesAsync( 403 base::Bind(&CookieManager::HasCookiesCompleted, 404 base::Unretained(this), 405 completion, 406 result)); 407 } 408 409 void CookieManager::HasCookiesCompleted(base::WaitableEvent* completion, 410 bool* result, 411 const CookieList& cookies) { 412 *result = cookies.size() != 0; 413 completion->Signal(); 414 } 415 416 bool CookieManager::AllowFileSchemeCookies() { 417 base::AutoLock lock(cookie_monster_lock_); 418 EnsureCookieMonsterExistsLocked(); 419 return AllowFileSchemeCookiesLocked(); 420 } 421 422 bool CookieManager::AllowFileSchemeCookiesLocked() { 423 return cookie_monster_->IsCookieableScheme(chrome::kFileScheme); 424 } 425 426 void CookieManager::SetAcceptFileSchemeCookies(bool accept) { 427 base::AutoLock lock(cookie_monster_lock_); 428 EnsureCookieMonsterExistsLocked(); 429 SetAcceptFileSchemeCookiesLocked(accept); 430 } 431 432 void CookieManager::SetAcceptFileSchemeCookiesLocked(bool accept) { 433 // The docs on CookieManager base class state the API must not be called after 434 // creating a CookieManager instance (which contradicts its own internal 435 // implementation) but this code does rely on the essence of that comment, as 436 // the monster will DCHECK here if it has already been lazy initialized (i.e. 437 // if cookies have been read or written from the store). If that turns out to 438 // be a problemin future, it looks like it maybe possible to relax the DCHECK. 439 cookie_monster_->SetEnableFileScheme(accept); 440 } 441 442 } // namespace 443 444 static void SetAcceptCookie(JNIEnv* env, jobject obj, jboolean accept) { 445 CookieManager::GetInstance()->SetAcceptCookie(accept); 446 } 447 448 static jboolean AcceptCookie(JNIEnv* env, jobject obj) { 449 return CookieManager::GetInstance()->AcceptCookie(); 450 } 451 452 static void SetCookie(JNIEnv* env, jobject obj, jstring url, jstring value) { 453 GURL host(ConvertJavaStringToUTF16(env, url)); 454 std::string cookie_value(ConvertJavaStringToUTF8(env, value)); 455 456 CookieManager::GetInstance()->SetCookie(host, cookie_value); 457 } 458 459 static jstring GetCookie(JNIEnv* env, jobject obj, jstring url) { 460 GURL host(ConvertJavaStringToUTF16(env, url)); 461 462 return base::android::ConvertUTF8ToJavaString( 463 env, 464 CookieManager::GetInstance()->GetCookie(host)).Release(); 465 } 466 467 static void RemoveSessionCookie(JNIEnv* env, jobject obj) { 468 CookieManager::GetInstance()->RemoveSessionCookie(); 469 } 470 471 static void RemoveAllCookie(JNIEnv* env, jobject obj) { 472 CookieManager::GetInstance()->RemoveAllCookie(); 473 } 474 475 static void RemoveExpiredCookie(JNIEnv* env, jobject obj) { 476 CookieManager::GetInstance()->RemoveExpiredCookie(); 477 } 478 479 static void FlushCookieStore(JNIEnv* env, jobject obj) { 480 CookieManager::GetInstance()->FlushCookieStore(); 481 } 482 483 static jboolean HasCookies(JNIEnv* env, jobject obj) { 484 return CookieManager::GetInstance()->HasCookies(); 485 } 486 487 static jboolean AllowFileSchemeCookies(JNIEnv* env, jobject obj) { 488 return CookieManager::GetInstance()->AllowFileSchemeCookies(); 489 } 490 491 static void SetAcceptFileSchemeCookies(JNIEnv* env, jobject obj, 492 jboolean accept) { 493 return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept); 494 } 495 496 scoped_refptr<net::CookieStore> CreateCookieStore( 497 AwBrowserContext* browser_context) { 498 return CookieManager::GetInstance()->CreateBrowserThreadCookieStore( 499 browser_context); 500 } 501 502 bool RegisterCookieManager(JNIEnv* env) { 503 return RegisterNativesImpl(env); 504 } 505 506 } // android_webview namespace 507