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 "jni/AwCookieManager_jni.h" 32 #include "net/cookies/cookie_monster.h" 33 #include "net/cookies/cookie_options.h" 34 #include "net/url_request/url_request_context.h" 35 #include "url/url_constants.h" 36 37 using base::FilePath; 38 using base::WaitableEvent; 39 using base::android::ConvertJavaStringToUTF8; 40 using base::android::ConvertJavaStringToUTF16; 41 using base::android::ScopedJavaGlobalRef; 42 using content::BrowserThread; 43 using net::CookieList; 44 using net::CookieMonster; 45 46 // In the future, we may instead want to inject an explicit CookieStore 47 // dependency into this object during process initialization to avoid 48 // depending on the URLRequestContext. 49 // See issue http://crbug.com/157683 50 51 // On the CookieManager methods without a callback and methods with a callback 52 // when that callback is null can be called from any thread, including threads 53 // without a message loop. Methods with a non-null callback must be called on 54 // a thread with a running message loop. 55 56 namespace android_webview { 57 58 namespace { 59 60 typedef base::Callback<void(bool)> BoolCallback; 61 typedef base::Callback<void(int)> IntCallback; 62 63 // Holds a Java BooleanCookieCallback, knows how to invoke it and turn it 64 // into a base callback. 65 class BoolCookieCallbackHolder { 66 public: 67 BoolCookieCallbackHolder(JNIEnv* env, jobject callback) { 68 callback_.Reset(env, callback); 69 } 70 71 void Invoke(bool result) { 72 if (!callback_.is_null()) { 73 JNIEnv* env = base::android::AttachCurrentThread(); 74 Java_AwCookieManager_invokeBooleanCookieCallback( 75 env, callback_.obj(), result); 76 } 77 } 78 79 static BoolCallback ConvertToCallback( 80 scoped_ptr<BoolCookieCallbackHolder> me) { 81 return base::Bind(&BoolCookieCallbackHolder::Invoke, 82 base::Owned(me.release())); 83 } 84 85 private: 86 ScopedJavaGlobalRef<jobject> callback_; 87 DISALLOW_COPY_AND_ASSIGN(BoolCookieCallbackHolder); 88 }; 89 90 // Construct a closure which signals a waitable event if and when the closure 91 // is called the waitable event must still exist. 92 static base::Closure SignalEventClosure(WaitableEvent* completion) { 93 return base::Bind(&WaitableEvent::Signal, base::Unretained(completion)); 94 } 95 96 static void DiscardBool(const base::Closure& f, bool b) { 97 f.Run(); 98 } 99 100 static BoolCallback BoolCallbackAdapter(const base::Closure& f) { 101 return base::Bind(&DiscardBool, f); 102 } 103 104 static void DiscardInt(const base::Closure& f, int i) { 105 f.Run(); 106 } 107 108 static IntCallback IntCallbackAdapter(const base::Closure& f) { 109 return base::Bind(&DiscardInt, f); 110 } 111 112 // Are cookies allowed for file:// URLs by default? 113 const bool kDefaultFileSchemeAllowed = false; 114 115 void ImportLegacyCookieStore(const FilePath& cookie_store_path) { 116 // We use the old cookie store to create the new cookie store only if the 117 // new cookie store does not exist. 118 if (base::PathExists(cookie_store_path)) 119 return; 120 121 // WebViewClassic gets the database path from Context and appends a 122 // hardcoded name. See: 123 // https://android.googlesource.com/platform/frameworks/base/+/bf6f6f9d/core/java/android/webkit/JniUtil.java 124 // https://android.googlesource.com/platform/external/webkit/+/7151e/ 125 // Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp 126 FilePath old_cookie_store_path; 127 base::android::GetDatabaseDirectory(&old_cookie_store_path); 128 old_cookie_store_path = old_cookie_store_path.Append( 129 FILE_PATH_LITERAL("webviewCookiesChromium.db")); 130 if (base::PathExists(old_cookie_store_path) && 131 !base::Move(old_cookie_store_path, cookie_store_path)) { 132 LOG(WARNING) << "Failed to move old cookie store path from " 133 << old_cookie_store_path.AsUTF8Unsafe() << " to " 134 << cookie_store_path.AsUTF8Unsafe(); 135 } 136 } 137 138 void GetUserDataDir(FilePath* user_data_dir) { 139 if (!PathService::Get(base::DIR_ANDROID_APP_DATA, user_data_dir)) { 140 NOTREACHED() << "Failed to get app data directory for Android WebView"; 141 } 142 } 143 144 class CookieManager { 145 public: 146 static CookieManager* GetInstance(); 147 148 scoped_refptr<net::CookieStore> GetCookieStore(); 149 150 void SetShouldAcceptCookies(bool accept); 151 bool GetShouldAcceptCookies(); 152 void SetCookie(const GURL& host, 153 const std::string& cookie_value, 154 scoped_ptr<BoolCookieCallbackHolder> callback); 155 void SetCookieSync(const GURL& host, 156 const std::string& cookie_value); 157 std::string GetCookie(const GURL& host); 158 void RemoveSessionCookies(scoped_ptr<BoolCookieCallbackHolder> callback); 159 void RemoveAllCookies(scoped_ptr<BoolCookieCallbackHolder> callback); 160 void RemoveAllCookiesSync(); 161 void RemoveSessionCookiesSync(); 162 void RemoveExpiredCookies(); 163 void FlushCookieStore(); 164 bool HasCookies(); 165 bool AllowFileSchemeCookies(); 166 void SetAcceptFileSchemeCookies(bool accept); 167 168 private: 169 friend struct base::DefaultLazyInstanceTraits<CookieManager>; 170 171 CookieManager(); 172 ~CookieManager(); 173 174 void ExecCookieTaskSync(const base::Callback<void(BoolCallback)>& task); 175 void ExecCookieTaskSync(const base::Callback<void(IntCallback)>& task); 176 void ExecCookieTaskSync(const base::Callback<void(base::Closure)>& task); 177 void ExecCookieTask(const base::Closure& task); 178 179 void SetCookieHelper( 180 const GURL& host, 181 const std::string& value, 182 BoolCallback callback); 183 184 void GetCookieValueAsyncHelper(const GURL& host, 185 std::string* result, 186 base::Closure complete); 187 void GetCookieValueCompleted(base::Closure complete, 188 std::string* result, 189 const std::string& value); 190 191 void RemoveSessionCookiesHelper(BoolCallback callback); 192 void RemoveAllCookiesHelper(BoolCallback callback); 193 void RemoveCookiesCompleted(BoolCallback callback, int num_deleted); 194 195 void FlushCookieStoreAsyncHelper(base::Closure complete); 196 197 void HasCookiesAsyncHelper(bool* result, base::Closure complete); 198 void HasCookiesCompleted(base::Closure complete, 199 bool* result, 200 const CookieList& cookies); 201 202 void CreateCookieMonster( 203 const FilePath& user_data_dir, 204 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, 205 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner); 206 void EnsureCookieMonsterExistsLocked(); 207 bool AllowFileSchemeCookiesLocked(); 208 void SetAcceptFileSchemeCookiesLocked(bool accept); 209 210 scoped_refptr<net::CookieMonster> cookie_monster_; 211 scoped_refptr<base::MessageLoopProxy> cookie_monster_proxy_; 212 base::Lock cookie_monster_lock_; 213 214 scoped_ptr<base::Thread> cookie_monster_client_thread_; 215 scoped_ptr<base::Thread> cookie_monster_backend_thread_; 216 217 DISALLOW_COPY_AND_ASSIGN(CookieManager); 218 }; 219 220 base::LazyInstance<CookieManager>::Leaky g_lazy_instance; 221 222 // static 223 CookieManager* CookieManager::GetInstance() { 224 return g_lazy_instance.Pointer(); 225 } 226 227 CookieManager::CookieManager() { 228 } 229 230 CookieManager::~CookieManager() { 231 } 232 233 void CookieManager::CreateCookieMonster( 234 const FilePath& user_data_dir, 235 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, 236 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) { 237 FilePath cookie_store_path = 238 user_data_dir.Append(FILE_PATH_LITERAL("Cookies")); 239 240 background_task_runner->PostTask( 241 FROM_HERE, 242 base::Bind(ImportLegacyCookieStore, cookie_store_path)); 243 244 content::CookieStoreConfig cookie_config( 245 cookie_store_path, 246 content::CookieStoreConfig::RESTORED_SESSION_COOKIES, 247 NULL, NULL); 248 cookie_config.client_task_runner = client_task_runner; 249 cookie_config.background_task_runner = background_task_runner; 250 net::CookieStore* cookie_store = content::CreateCookieStore(cookie_config); 251 cookie_monster_ = cookie_store->GetCookieMonster(); 252 SetAcceptFileSchemeCookiesLocked(kDefaultFileSchemeAllowed); 253 } 254 255 void CookieManager::EnsureCookieMonsterExistsLocked() { 256 cookie_monster_lock_.AssertAcquired(); 257 if (cookie_monster_.get()) { 258 return; 259 } 260 261 // Create cookie monster using WebView-specific threads, as the rest of the 262 // browser has not been started yet. 263 FilePath user_data_dir; 264 GetUserDataDir(&user_data_dir); 265 cookie_monster_client_thread_.reset( 266 new base::Thread("CookieMonsterClient")); 267 cookie_monster_client_thread_->Start(); 268 cookie_monster_proxy_ = cookie_monster_client_thread_->message_loop_proxy(); 269 cookie_monster_backend_thread_.reset( 270 new base::Thread("CookieMonsterBackend")); 271 cookie_monster_backend_thread_->Start(); 272 273 CreateCookieMonster(user_data_dir, 274 cookie_monster_proxy_, 275 cookie_monster_backend_thread_->message_loop_proxy()); 276 } 277 278 // Executes the |task| on the |cookie_monster_proxy_| message loop and 279 // waits for it to complete before returning. 280 281 // To execute a CookieTask synchronously you must arrange for Signal to be 282 // called on the waitable event at some point. You can call the bool or int 283 // versions of ExecCookieTaskSync, these will supply the caller with a dummy 284 // callback which takes an int/bool, throws it away and calls Signal. 285 // Alternatively you can call the version which supplies a Closure in which 286 // case you must call Run on it when you want the unblock the calling code. 287 288 // Ignore a bool callback. 289 void CookieManager::ExecCookieTaskSync( 290 const base::Callback<void(BoolCallback)>& task) { 291 WaitableEvent completion(false, false); 292 ExecCookieTask( 293 base::Bind(task, BoolCallbackAdapter(SignalEventClosure(&completion)))); 294 ScopedAllowWaitForLegacyWebViewApi wait; 295 completion.Wait(); 296 } 297 298 // Ignore an int callback. 299 void CookieManager::ExecCookieTaskSync( 300 const base::Callback<void(IntCallback)>& task) { 301 WaitableEvent completion(false, false); 302 ExecCookieTask( 303 base::Bind(task, IntCallbackAdapter(SignalEventClosure(&completion)))); 304 ScopedAllowWaitForLegacyWebViewApi wait; 305 completion.Wait(); 306 } 307 308 // Call the supplied closure when you want to signal that the blocked code can 309 // continue. 310 void CookieManager::ExecCookieTaskSync( 311 const base::Callback<void(base::Closure)>& task) { 312 WaitableEvent completion(false, false); 313 ExecCookieTask(base::Bind(task, SignalEventClosure(&completion))); 314 ScopedAllowWaitForLegacyWebViewApi wait; 315 completion.Wait(); 316 } 317 318 // Executes the |task| on the |cookie_monster_proxy_| message loop. 319 void CookieManager::ExecCookieTask(const base::Closure& task) { 320 base::AutoLock lock(cookie_monster_lock_); 321 EnsureCookieMonsterExistsLocked(); 322 cookie_monster_proxy_->PostTask(FROM_HERE, task); 323 } 324 325 scoped_refptr<net::CookieStore> CookieManager::GetCookieStore() { 326 base::AutoLock lock(cookie_monster_lock_); 327 EnsureCookieMonsterExistsLocked(); 328 return cookie_monster_; 329 } 330 331 void CookieManager::SetShouldAcceptCookies(bool accept) { 332 AwCookieAccessPolicy::GetInstance()->SetShouldAcceptCookies(accept); 333 } 334 335 bool CookieManager::GetShouldAcceptCookies() { 336 return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies(); 337 } 338 339 void CookieManager::SetCookie( 340 const GURL& host, 341 const std::string& cookie_value, 342 scoped_ptr<BoolCookieCallbackHolder> callback_holder) { 343 BoolCallback callback = 344 BoolCookieCallbackHolder::ConvertToCallback(callback_holder.Pass()); 345 ExecCookieTask(base::Bind(&CookieManager::SetCookieHelper, 346 base::Unretained(this), 347 host, 348 cookie_value, 349 callback)); 350 } 351 352 void CookieManager::SetCookieSync(const GURL& host, 353 const std::string& cookie_value) { 354 ExecCookieTaskSync(base::Bind(&CookieManager::SetCookieHelper, 355 base::Unretained(this), 356 host, 357 cookie_value)); 358 } 359 360 void CookieManager::SetCookieHelper( 361 const GURL& host, 362 const std::string& value, 363 const BoolCallback callback) { 364 net::CookieOptions options; 365 options.set_include_httponly(); 366 367 cookie_monster_->SetCookieWithOptionsAsync( 368 host, value, options, callback); 369 } 370 371 std::string CookieManager::GetCookie(const GURL& host) { 372 std::string cookie_value; 373 ExecCookieTaskSync(base::Bind(&CookieManager::GetCookieValueAsyncHelper, 374 base::Unretained(this), 375 host, 376 &cookie_value)); 377 return cookie_value; 378 } 379 380 void CookieManager::GetCookieValueAsyncHelper( 381 const GURL& host, 382 std::string* result, 383 base::Closure complete) { 384 net::CookieOptions options; 385 options.set_include_httponly(); 386 387 cookie_monster_->GetCookiesWithOptionsAsync( 388 host, 389 options, 390 base::Bind(&CookieManager::GetCookieValueCompleted, 391 base::Unretained(this), 392 complete, 393 result)); 394 } 395 396 void CookieManager::GetCookieValueCompleted(base::Closure complete, 397 std::string* result, 398 const std::string& value) { 399 *result = value; 400 complete.Run(); 401 } 402 403 void CookieManager::RemoveSessionCookies( 404 scoped_ptr<BoolCookieCallbackHolder> callback_holder) { 405 BoolCallback callback = 406 BoolCookieCallbackHolder::ConvertToCallback(callback_holder.Pass()); 407 ExecCookieTask(base::Bind(&CookieManager::RemoveSessionCookiesHelper, 408 base::Unretained(this), 409 callback)); 410 } 411 412 void CookieManager::RemoveSessionCookiesSync() { 413 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveSessionCookiesHelper, 414 base::Unretained(this))); 415 } 416 417 void CookieManager::RemoveSessionCookiesHelper( 418 BoolCallback callback) { 419 cookie_monster_->DeleteSessionCookiesAsync( 420 base::Bind(&CookieManager::RemoveCookiesCompleted, 421 base::Unretained(this), 422 callback)); 423 } 424 425 void CookieManager::RemoveCookiesCompleted( 426 BoolCallback callback, 427 int num_deleted) { 428 callback.Run(num_deleted > 0); 429 } 430 431 void CookieManager::RemoveAllCookies( 432 scoped_ptr<BoolCookieCallbackHolder> callback_holder) { 433 BoolCallback callback = 434 BoolCookieCallbackHolder::ConvertToCallback(callback_holder.Pass()); 435 ExecCookieTask(base::Bind(&CookieManager::RemoveAllCookiesHelper, 436 base::Unretained(this), 437 callback)); 438 } 439 440 void CookieManager::RemoveAllCookiesSync() { 441 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveAllCookiesHelper, 442 base::Unretained(this))); 443 } 444 445 void CookieManager::RemoveAllCookiesHelper( 446 const BoolCallback callback) { 447 cookie_monster_->DeleteAllAsync( 448 base::Bind(&CookieManager::RemoveCookiesCompleted, 449 base::Unretained(this), 450 callback)); 451 } 452 453 void CookieManager::RemoveExpiredCookies() { 454 // HasCookies will call GetAllCookiesAsync, which in turn will force a GC. 455 HasCookies(); 456 } 457 458 void CookieManager::FlushCookieStore() { 459 ExecCookieTaskSync(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper, 460 base::Unretained(this))); 461 } 462 463 void CookieManager::FlushCookieStoreAsyncHelper( 464 base::Closure complete) { 465 cookie_monster_->FlushStore(complete); 466 } 467 468 bool CookieManager::HasCookies() { 469 bool has_cookies; 470 ExecCookieTaskSync(base::Bind(&CookieManager::HasCookiesAsyncHelper, 471 base::Unretained(this), 472 &has_cookies)); 473 return has_cookies; 474 } 475 476 // TODO(kristianm): Simplify this, copying the entire list around 477 // should not be needed. 478 void CookieManager::HasCookiesAsyncHelper(bool* result, 479 base::Closure complete) { 480 cookie_monster_->GetAllCookiesAsync( 481 base::Bind(&CookieManager::HasCookiesCompleted, 482 base::Unretained(this), 483 complete, 484 result)); 485 } 486 487 void CookieManager::HasCookiesCompleted(base::Closure complete, 488 bool* result, 489 const CookieList& cookies) { 490 *result = cookies.size() != 0; 491 complete.Run(); 492 } 493 494 bool CookieManager::AllowFileSchemeCookies() { 495 base::AutoLock lock(cookie_monster_lock_); 496 EnsureCookieMonsterExistsLocked(); 497 return AllowFileSchemeCookiesLocked(); 498 } 499 500 bool CookieManager::AllowFileSchemeCookiesLocked() { 501 return cookie_monster_->IsCookieableScheme(url::kFileScheme); 502 } 503 504 void CookieManager::SetAcceptFileSchemeCookies(bool accept) { 505 base::AutoLock lock(cookie_monster_lock_); 506 EnsureCookieMonsterExistsLocked(); 507 SetAcceptFileSchemeCookiesLocked(accept); 508 } 509 510 void CookieManager::SetAcceptFileSchemeCookiesLocked(bool accept) { 511 // The docs on CookieManager base class state the API must not be called after 512 // creating a CookieManager instance (which contradicts its own internal 513 // implementation) but this code does rely on the essence of that comment, as 514 // the monster will DCHECK here if it has already been lazy initialized (i.e. 515 // if cookies have been read or written from the store). If that turns out to 516 // be a problemin future, it looks like it maybe possible to relax the DCHECK. 517 cookie_monster_->SetEnableFileScheme(accept); 518 } 519 520 } // namespace 521 522 static void SetShouldAcceptCookies(JNIEnv* env, jobject obj, jboolean accept) { 523 CookieManager::GetInstance()->SetShouldAcceptCookies(accept); 524 } 525 526 static jboolean GetShouldAcceptCookies(JNIEnv* env, jobject obj) { 527 return CookieManager::GetInstance()->GetShouldAcceptCookies(); 528 } 529 530 static void SetCookie(JNIEnv* env, 531 jobject obj, 532 jstring url, 533 jstring value, 534 jobject java_callback) { 535 GURL host(ConvertJavaStringToUTF16(env, url)); 536 std::string cookie_value(ConvertJavaStringToUTF8(env, value)); 537 scoped_ptr<BoolCookieCallbackHolder> callback( 538 new BoolCookieCallbackHolder(env, java_callback)); 539 CookieManager::GetInstance()->SetCookie(host, cookie_value, callback.Pass()); 540 } 541 542 static void SetCookieSync(JNIEnv* env, 543 jobject obj, 544 jstring url, 545 jstring value) { 546 GURL host(ConvertJavaStringToUTF16(env, url)); 547 std::string cookie_value(ConvertJavaStringToUTF8(env, value)); 548 549 CookieManager::GetInstance()->SetCookieSync(host, cookie_value); 550 } 551 552 static jstring GetCookie(JNIEnv* env, jobject obj, jstring url) { 553 GURL host(ConvertJavaStringToUTF16(env, url)); 554 555 return base::android::ConvertUTF8ToJavaString( 556 env, 557 CookieManager::GetInstance()->GetCookie(host)).Release(); 558 } 559 560 static void RemoveSessionCookies(JNIEnv* env, 561 jobject obj, 562 jobject java_callback) { 563 scoped_ptr<BoolCookieCallbackHolder> callback( 564 new BoolCookieCallbackHolder(env, java_callback)); 565 CookieManager::GetInstance()->RemoveSessionCookies(callback.Pass()); 566 } 567 568 static void RemoveSessionCookiesSync(JNIEnv* env, jobject obj) { 569 CookieManager::GetInstance()->RemoveSessionCookiesSync(); 570 } 571 572 static void RemoveAllCookies(JNIEnv* env, jobject obj, jobject java_callback) { 573 scoped_ptr<BoolCookieCallbackHolder> callback( 574 new BoolCookieCallbackHolder(env, java_callback)); 575 CookieManager::GetInstance()->RemoveAllCookies(callback.Pass()); 576 } 577 578 static void RemoveAllCookiesSync(JNIEnv* env, jobject obj) { 579 CookieManager::GetInstance()->RemoveAllCookiesSync(); 580 } 581 582 static void RemoveExpiredCookies(JNIEnv* env, jobject obj) { 583 CookieManager::GetInstance()->RemoveExpiredCookies(); 584 } 585 586 static void FlushCookieStore(JNIEnv* env, jobject obj) { 587 CookieManager::GetInstance()->FlushCookieStore(); 588 } 589 590 static jboolean HasCookies(JNIEnv* env, jobject obj) { 591 return CookieManager::GetInstance()->HasCookies(); 592 } 593 594 static jboolean AllowFileSchemeCookies(JNIEnv* env, jobject obj) { 595 return CookieManager::GetInstance()->AllowFileSchemeCookies(); 596 } 597 598 static void SetAcceptFileSchemeCookies(JNIEnv* env, jobject obj, 599 jboolean accept) { 600 return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept); 601 } 602 603 scoped_refptr<net::CookieStore> CreateCookieStore( 604 AwBrowserContext* browser_context) { 605 return CookieManager::GetInstance()->GetCookieStore(); 606 } 607 608 bool RegisterCookieManager(JNIEnv* env) { 609 return RegisterNativesImpl(env); 610 } 611 612 } // android_webview namespace 613