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