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/files/file_path.h" 17#include "base/files/file_util.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 37using base::FilePath; 38using base::WaitableEvent; 39using base::android::ConvertJavaStringToUTF8; 40using base::android::ConvertJavaStringToUTF16; 41using base::android::ScopedJavaGlobalRef; 42using content::BrowserThread; 43using net::CookieList; 44using 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 56namespace android_webview { 57 58namespace { 59 60typedef base::Callback<void(bool)> BoolCallback; 61typedef base::Callback<void(int)> IntCallback; 62 63// Holds a Java BooleanCookieCallback, knows how to invoke it and turn it 64// into a base callback. 65class 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. 92static base::Closure SignalEventClosure(WaitableEvent* completion) { 93 return base::Bind(&WaitableEvent::Signal, base::Unretained(completion)); 94} 95 96static void DiscardBool(const base::Closure& f, bool b) { 97 f.Run(); 98} 99 100static BoolCallback BoolCallbackAdapter(const base::Closure& f) { 101 return base::Bind(&DiscardBool, f); 102} 103 104static void DiscardInt(const base::Closure& f, int i) { 105 f.Run(); 106} 107 108static IntCallback IntCallbackAdapter(const base::Closure& f) { 109 return base::Bind(&DiscardInt, f); 110} 111 112// Are cookies allowed for file:// URLs by default? 113const bool kDefaultFileSchemeAllowed = false; 114 115void 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 138void 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 144class 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 220base::LazyInstance<CookieManager>::Leaky g_lazy_instance; 221 222// static 223CookieManager* CookieManager::GetInstance() { 224 return g_lazy_instance.Pointer(); 225} 226 227CookieManager::CookieManager() { 228} 229 230CookieManager::~CookieManager() { 231} 232 233void 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 255void 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. 289void 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. 299void 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. 310void 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. 319void 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 325scoped_refptr<net::CookieStore> CookieManager::GetCookieStore() { 326 base::AutoLock lock(cookie_monster_lock_); 327 EnsureCookieMonsterExistsLocked(); 328 return cookie_monster_; 329} 330 331void CookieManager::SetShouldAcceptCookies(bool accept) { 332 AwCookieAccessPolicy::GetInstance()->SetShouldAcceptCookies(accept); 333} 334 335bool CookieManager::GetShouldAcceptCookies() { 336 return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies(); 337} 338 339void 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 352void 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 360void 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 371std::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 380void 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 396void CookieManager::GetCookieValueCompleted(base::Closure complete, 397 std::string* result, 398 const std::string& value) { 399 *result = value; 400 complete.Run(); 401} 402 403void 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 412void CookieManager::RemoveSessionCookiesSync() { 413 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveSessionCookiesHelper, 414 base::Unretained(this))); 415} 416 417void CookieManager::RemoveSessionCookiesHelper( 418 BoolCallback callback) { 419 cookie_monster_->DeleteSessionCookiesAsync( 420 base::Bind(&CookieManager::RemoveCookiesCompleted, 421 base::Unretained(this), 422 callback)); 423} 424 425void CookieManager::RemoveCookiesCompleted( 426 BoolCallback callback, 427 int num_deleted) { 428 callback.Run(num_deleted > 0); 429} 430 431void 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 440void CookieManager::RemoveAllCookiesSync() { 441 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveAllCookiesHelper, 442 base::Unretained(this))); 443} 444 445void CookieManager::RemoveAllCookiesHelper( 446 const BoolCallback callback) { 447 cookie_monster_->DeleteAllAsync( 448 base::Bind(&CookieManager::RemoveCookiesCompleted, 449 base::Unretained(this), 450 callback)); 451} 452 453void CookieManager::RemoveExpiredCookies() { 454 // HasCookies will call GetAllCookiesAsync, which in turn will force a GC. 455 HasCookies(); 456} 457 458void CookieManager::FlushCookieStore() { 459 ExecCookieTaskSync(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper, 460 base::Unretained(this))); 461} 462 463void CookieManager::FlushCookieStoreAsyncHelper( 464 base::Closure complete) { 465 cookie_monster_->FlushStore(complete); 466} 467 468bool 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. 478void 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 487void CookieManager::HasCookiesCompleted(base::Closure complete, 488 bool* result, 489 const CookieList& cookies) { 490 *result = cookies.size() != 0; 491 complete.Run(); 492} 493 494bool CookieManager::AllowFileSchemeCookies() { 495 base::AutoLock lock(cookie_monster_lock_); 496 EnsureCookieMonsterExistsLocked(); 497 return AllowFileSchemeCookiesLocked(); 498} 499 500bool CookieManager::AllowFileSchemeCookiesLocked() { 501 return cookie_monster_->IsCookieableScheme(url::kFileScheme); 502} 503 504void CookieManager::SetAcceptFileSchemeCookies(bool accept) { 505 base::AutoLock lock(cookie_monster_lock_); 506 EnsureCookieMonsterExistsLocked(); 507 SetAcceptFileSchemeCookiesLocked(accept); 508} 509 510void 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 522static void SetShouldAcceptCookies(JNIEnv* env, jobject obj, jboolean accept) { 523 CookieManager::GetInstance()->SetShouldAcceptCookies(accept); 524} 525 526static jboolean GetShouldAcceptCookies(JNIEnv* env, jobject obj) { 527 return CookieManager::GetInstance()->GetShouldAcceptCookies(); 528} 529 530static 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 542static 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 552static 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 560static 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 568static void RemoveSessionCookiesSync(JNIEnv* env, jobject obj) { 569 CookieManager::GetInstance()->RemoveSessionCookiesSync(); 570} 571 572static 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 578static void RemoveAllCookiesSync(JNIEnv* env, jobject obj) { 579 CookieManager::GetInstance()->RemoveAllCookiesSync(); 580} 581 582static void RemoveExpiredCookies(JNIEnv* env, jobject obj) { 583 CookieManager::GetInstance()->RemoveExpiredCookies(); 584} 585 586static void FlushCookieStore(JNIEnv* env, jobject obj) { 587 CookieManager::GetInstance()->FlushCookieStore(); 588} 589 590static jboolean HasCookies(JNIEnv* env, jobject obj) { 591 return CookieManager::GetInstance()->HasCookies(); 592} 593 594static jboolean AllowFileSchemeCookies(JNIEnv* env, jobject obj) { 595 return CookieManager::GetInstance()->AllowFileSchemeCookies(); 596} 597 598static void SetAcceptFileSchemeCookies(JNIEnv* env, jobject obj, 599 jboolean accept) { 600 return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept); 601} 602 603scoped_refptr<net::CookieStore> CreateCookieStore( 604 AwBrowserContext* browser_context) { 605 return CookieManager::GetInstance()->GetCookieStore(); 606} 607 608bool RegisterCookieManager(JNIEnv* env) { 609 return RegisterNativesImpl(env); 610} 611 612} // android_webview namespace 613