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.
5 #include "android_webview/native/cookie_manager.h"
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"
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
;
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
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.
56 namespace android_webview
{
60 typedef base::Callback
<void(bool)> BoolCallback
;
61 typedef base::Callback
<void(int)> IntCallback
;
63 // Holds a Java BooleanCookieCallback, knows how to invoke it and turn it
64 // into a base callback.
65 class BoolCookieCallbackHolder
{
67 BoolCookieCallbackHolder(JNIEnv
* env
, jobject callback
) {
68 callback_
.Reset(env
, callback
);
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
);
79 static BoolCallback
ConvertToCallback(
80 scoped_ptr
<BoolCookieCallbackHolder
> me
) {
81 return base::Bind(&BoolCookieCallbackHolder::Invoke
,
82 base::Owned(me
.release()));
86 ScopedJavaGlobalRef
<jobject
> callback_
;
87 DISALLOW_COPY_AND_ASSIGN(BoolCookieCallbackHolder
);
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
));
96 static void DiscardBool(const base::Closure
& f
, bool b
) {
100 static BoolCallback
BoolCallbackAdapter(const base::Closure
& f
) {
101 return base::Bind(&DiscardBool
, f
);
104 static void DiscardInt(const base::Closure
& f
, int i
) {
108 static IntCallback
IntCallbackAdapter(const base::Closure
& f
) {
109 return base::Bind(&DiscardInt
, f
);
112 // Are cookies allowed for file:// URLs by default?
113 const bool kDefaultFileSchemeAllowed
= false;
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
))
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();
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";
144 class CookieManager
{
146 static CookieManager
* GetInstance();
148 scoped_refptr
<net::CookieStore
> GetCookieStore();
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();
165 bool AllowFileSchemeCookies();
166 void SetAcceptFileSchemeCookies(bool accept
);
169 friend struct base::DefaultLazyInstanceTraits
<CookieManager
>;
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
);
179 void SetCookieHelper(
181 const std::string
& value
,
182 BoolCallback callback
);
184 void GetCookieValueAsyncHelper(const GURL
& host
,
186 base::Closure complete
);
187 void GetCookieValueCompleted(base::Closure complete
,
189 const std::string
& value
);
191 void RemoveSessionCookiesHelper(BoolCallback callback
);
192 void RemoveAllCookiesHelper(BoolCallback callback
);
193 void RemoveCookiesCompleted(BoolCallback callback
, int num_deleted
);
195 void FlushCookieStoreAsyncHelper(base::Closure complete
);
197 void HasCookiesAsyncHelper(bool* result
, base::Closure complete
);
198 void HasCookiesCompleted(base::Closure complete
,
200 const CookieList
& cookies
);
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
);
210 scoped_refptr
<net::CookieMonster
> cookie_monster_
;
211 scoped_refptr
<base::MessageLoopProxy
> cookie_monster_proxy_
;
212 base::Lock cookie_monster_lock_
;
214 scoped_ptr
<base::Thread
> cookie_monster_client_thread_
;
215 scoped_ptr
<base::Thread
> cookie_monster_backend_thread_
;
217 DISALLOW_COPY_AND_ASSIGN(CookieManager
);
220 base::LazyInstance
<CookieManager
>::Leaky g_lazy_instance
;
223 CookieManager
* CookieManager::GetInstance() {
224 return g_lazy_instance
.Pointer();
227 CookieManager::CookieManager() {
230 CookieManager::~CookieManager() {
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"));
240 background_task_runner
->PostTask(
242 base::Bind(ImportLegacyCookieStore
, cookie_store_path
));
244 content::CookieStoreConfig
cookie_config(
246 content::CookieStoreConfig::RESTORED_SESSION_COOKIES
,
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
);
255 void CookieManager::EnsureCookieMonsterExistsLocked() {
256 cookie_monster_lock_
.AssertAcquired();
257 if (cookie_monster_
.get()) {
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();
273 CreateCookieMonster(user_data_dir
,
274 cookie_monster_proxy_
,
275 cookie_monster_backend_thread_
->message_loop_proxy());
278 // Executes the |task| on the |cookie_monster_proxy_| message loop and
279 // waits for it to complete before returning.
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.
288 // Ignore a bool callback.
289 void CookieManager::ExecCookieTaskSync(
290 const base::Callback
<void(BoolCallback
)>& task
) {
291 WaitableEvent
completion(false, false);
293 base::Bind(task
, BoolCallbackAdapter(SignalEventClosure(&completion
))));
294 ScopedAllowWaitForLegacyWebViewApi wait
;
298 // Ignore an int callback.
299 void CookieManager::ExecCookieTaskSync(
300 const base::Callback
<void(IntCallback
)>& task
) {
301 WaitableEvent
completion(false, false);
303 base::Bind(task
, IntCallbackAdapter(SignalEventClosure(&completion
))));
304 ScopedAllowWaitForLegacyWebViewApi wait
;
308 // Call the supplied closure when you want to signal that the blocked code can
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
;
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
);
325 scoped_refptr
<net::CookieStore
> CookieManager::GetCookieStore() {
326 base::AutoLock
lock(cookie_monster_lock_
);
327 EnsureCookieMonsterExistsLocked();
328 return cookie_monster_
;
331 void CookieManager::SetShouldAcceptCookies(bool accept
) {
332 AwCookieAccessPolicy::GetInstance()->SetShouldAcceptCookies(accept
);
335 bool CookieManager::GetShouldAcceptCookies() {
336 return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies();
339 void CookieManager::SetCookie(
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),
352 void CookieManager::SetCookieSync(const GURL
& host
,
353 const std::string
& cookie_value
) {
354 ExecCookieTaskSync(base::Bind(&CookieManager::SetCookieHelper
,
355 base::Unretained(this),
360 void CookieManager::SetCookieHelper(
362 const std::string
& value
,
363 const BoolCallback callback
) {
364 net::CookieOptions options
;
365 options
.set_include_httponly();
367 cookie_monster_
->SetCookieWithOptionsAsync(
368 host
, value
, options
, callback
);
371 std::string
CookieManager::GetCookie(const GURL
& host
) {
372 std::string cookie_value
;
373 ExecCookieTaskSync(base::Bind(&CookieManager::GetCookieValueAsyncHelper
,
374 base::Unretained(this),
380 void CookieManager::GetCookieValueAsyncHelper(
383 base::Closure complete
) {
384 net::CookieOptions options
;
385 options
.set_include_httponly();
387 cookie_monster_
->GetCookiesWithOptionsAsync(
390 base::Bind(&CookieManager::GetCookieValueCompleted
,
391 base::Unretained(this),
396 void CookieManager::GetCookieValueCompleted(base::Closure complete
,
398 const std::string
& value
) {
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),
412 void CookieManager::RemoveSessionCookiesSync() {
413 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveSessionCookiesHelper
,
414 base::Unretained(this)));
417 void CookieManager::RemoveSessionCookiesHelper(
418 BoolCallback callback
) {
419 cookie_monster_
->DeleteSessionCookiesAsync(
420 base::Bind(&CookieManager::RemoveCookiesCompleted
,
421 base::Unretained(this),
425 void CookieManager::RemoveCookiesCompleted(
426 BoolCallback callback
,
428 callback
.Run(num_deleted
> 0);
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),
440 void CookieManager::RemoveAllCookiesSync() {
441 ExecCookieTaskSync(base::Bind(&CookieManager::RemoveAllCookiesHelper
,
442 base::Unretained(this)));
445 void CookieManager::RemoveAllCookiesHelper(
446 const BoolCallback callback
) {
447 cookie_monster_
->DeleteAllAsync(
448 base::Bind(&CookieManager::RemoveCookiesCompleted
,
449 base::Unretained(this),
453 void CookieManager::RemoveExpiredCookies() {
454 // HasCookies will call GetAllCookiesAsync, which in turn will force a GC.
458 void CookieManager::FlushCookieStore() {
459 ExecCookieTaskSync(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper
,
460 base::Unretained(this)));
463 void CookieManager::FlushCookieStoreAsyncHelper(
464 base::Closure complete
) {
465 cookie_monster_
->FlushStore(complete
);
468 bool CookieManager::HasCookies() {
470 ExecCookieTaskSync(base::Bind(&CookieManager::HasCookiesAsyncHelper
,
471 base::Unretained(this),
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),
487 void CookieManager::HasCookiesCompleted(base::Closure complete
,
489 const CookieList
& cookies
) {
490 *result
= cookies
.size() != 0;
494 bool CookieManager::AllowFileSchemeCookies() {
495 base::AutoLock
lock(cookie_monster_lock_
);
496 EnsureCookieMonsterExistsLocked();
497 return AllowFileSchemeCookiesLocked();
500 bool CookieManager::AllowFileSchemeCookiesLocked() {
501 return cookie_monster_
->IsCookieableScheme(url::kFileScheme
);
504 void CookieManager::SetAcceptFileSchemeCookies(bool accept
) {
505 base::AutoLock
lock(cookie_monster_lock_
);
506 EnsureCookieMonsterExistsLocked();
507 SetAcceptFileSchemeCookiesLocked(accept
);
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
);
522 static void SetShouldAcceptCookies(JNIEnv
* env
, jobject obj
, jboolean accept
) {
523 CookieManager::GetInstance()->SetShouldAcceptCookies(accept
);
526 static jboolean
GetShouldAcceptCookies(JNIEnv
* env
, jobject obj
) {
527 return CookieManager::GetInstance()->GetShouldAcceptCookies();
530 static void SetCookie(JNIEnv
* env
,
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());
542 static void SetCookieSync(JNIEnv
* env
,
546 GURL
host(ConvertJavaStringToUTF16(env
, url
));
547 std::string
cookie_value(ConvertJavaStringToUTF8(env
, value
));
549 CookieManager::GetInstance()->SetCookieSync(host
, cookie_value
);
552 static jstring
GetCookie(JNIEnv
* env
, jobject obj
, jstring url
) {
553 GURL
host(ConvertJavaStringToUTF16(env
, url
));
555 return base::android::ConvertUTF8ToJavaString(
557 CookieManager::GetInstance()->GetCookie(host
)).Release();
560 static void RemoveSessionCookies(JNIEnv
* env
,
562 jobject java_callback
) {
563 scoped_ptr
<BoolCookieCallbackHolder
> callback(
564 new BoolCookieCallbackHolder(env
, java_callback
));
565 CookieManager::GetInstance()->RemoveSessionCookies(callback
.Pass());
568 static void RemoveSessionCookiesSync(JNIEnv
* env
, jobject obj
) {
569 CookieManager::GetInstance()->RemoveSessionCookiesSync();
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());
578 static void RemoveAllCookiesSync(JNIEnv
* env
, jobject obj
) {
579 CookieManager::GetInstance()->RemoveAllCookiesSync();
582 static void RemoveExpiredCookies(JNIEnv
* env
, jobject obj
) {
583 CookieManager::GetInstance()->RemoveExpiredCookies();
586 static void FlushCookieStore(JNIEnv
* env
, jobject obj
) {
587 CookieManager::GetInstance()->FlushCookieStore();
590 static jboolean
HasCookies(JNIEnv
* env
, jobject obj
) {
591 return CookieManager::GetInstance()->HasCookies();
594 static jboolean
AllowFileSchemeCookies(JNIEnv
* env
, jobject obj
) {
595 return CookieManager::GetInstance()->AllowFileSchemeCookies();
598 static void SetAcceptFileSchemeCookies(JNIEnv
* env
, jobject obj
,
600 return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept
);
603 scoped_refptr
<net::CookieStore
> CreateCookieStore(
604 AwBrowserContext
* browser_context
) {
605 return CookieManager::GetInstance()->GetCookieStore();
608 bool RegisterCookieManager(JNIEnv
* env
) {
609 return RegisterNativesImpl(env
);
612 } // android_webview namespace