Home | History | Annotate | Download | only in native
      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