Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / android_webview / native / cookie_manager.cc
index cd43f30..adb0ec8 100644 (file)
@@ -4,25 +4,37 @@
 
 #include "android_webview/native/cookie_manager.h"
 
+#include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_cookie_access_policy.h"
+#include "android_webview/browser/net/init_native_callback.h"
 #include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h"
 #include "android_webview/native/aw_browser_dependency_factory.h"
 #include "base/android/jni_string.h"
+#include "base/android/path_utils.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cookie_crypto_delegate.h"
+#include "content/public/browser/cookie_store_factory.h"
 #include "content/public/common/url_constants.h"
 #include "jni/AwCookieManager_jni.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_options.h"
 #include "net/url_request/url_request_context.h"
 
+using base::FilePath;
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertJavaStringToUTF16;
 using content::BrowserThread;
@@ -43,14 +55,49 @@ namespace android_webview {
 
 namespace {
 
+// Are cookies allowed for file:// URLs by default?
+const bool kDefaultFileSchemeAllowed = false;
+
+void ImportLegacyCookieStore(const FilePath& cookie_store_path) {
+  // We use the old cookie store to create the new cookie store only if the
+  // new cookie store does not exist.
+  if (base::PathExists(cookie_store_path))
+    return;
+
+  // WebViewClassic gets the database path from Context and appends a
+  // hardcoded name. See:
+  // https://android.googlesource.com/platform/frameworks/base/+/bf6f6f9d/core/java/android/webkit/JniUtil.java
+  // https://android.googlesource.com/platform/external/webkit/+/7151e/
+  //     Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
+  FilePath old_cookie_store_path;
+  base::android::GetDatabaseDirectory(&old_cookie_store_path);
+  old_cookie_store_path = old_cookie_store_path.Append(
+      FILE_PATH_LITERAL("webviewCookiesChromium.db"));
+  if (base::PathExists(old_cookie_store_path) &&
+      !base::Move(old_cookie_store_path, cookie_store_path)) {
+    LOG(WARNING) << "Failed to move old cookie store path from "
+                 << old_cookie_store_path.AsUTF8Unsafe() << " to "
+                 << cookie_store_path.AsUTF8Unsafe();
+  }
+}
+
+void GetUserDataDir(FilePath* user_data_dir) {
+  if (!PathService::Get(base::DIR_ANDROID_APP_DATA, user_data_dir)) {
+    NOTREACHED() << "Failed to get app data directory for Android WebView";
+  }
+}
+
 class CookieManager {
  public:
   static CookieManager* GetInstance();
 
-  void SetCookieMonster(net::CookieMonster* cookie_monster);
+  scoped_refptr<net::CookieStore> CreateBrowserThreadCookieStore(
+      AwBrowserContext* browser_context);
 
   void SetAcceptCookie(bool accept);
   bool AcceptCookie();
+  void SetAcceptThirdPartyCookie(bool accept);
+  bool AcceptThirdPartyCookie();
   void SetCookie(const GURL& host, const std::string& cookie_value);
   std::string GetCookie(const GURL& host);
   void RemoveSessionCookie();
@@ -68,14 +115,13 @@ class CookieManager {
   ~CookieManager();
 
   typedef base::Callback<void(base::WaitableEvent*)> CookieTask;
-  void ExecCookieTask(const CookieTask& task,
-                      const bool wait_for_completion);
+  void ExecCookieTask(const CookieTask& task);
 
   void SetCookieAsyncHelper(
       const GURL& host,
       const std::string& value,
       base::WaitableEvent* completion);
-  void SetCookieCompleted(bool success);
+  void SetCookieCompleted(base::WaitableEvent* completion, bool success);
 
   void GetCookieValueAsyncHelper(
       const GURL& host,
@@ -87,7 +133,7 @@ class CookieManager {
 
   void RemoveSessionCookieAsyncHelper(base::WaitableEvent* completion);
   void RemoveAllCookieAsyncHelper(base::WaitableEvent* completion);
-  void RemoveCookiesCompleted(int num_deleted);
+  void RemoveCookiesCompleted(base::WaitableEvent* completion, int num_deleted);
 
   void FlushCookieStoreAsyncHelper(base::WaitableEvent* completion);
 
@@ -97,7 +143,22 @@ class CookieManager {
                            bool* result,
                            const CookieList& cookies);
 
+  void CreateCookieMonster(
+    const FilePath& user_data_dir,
+    const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner);
+  void EnsureCookieMonsterExistsLocked();
+  bool AllowFileSchemeCookiesLocked();
+  void SetAcceptFileSchemeCookiesLocked(bool accept);
+
   scoped_refptr<net::CookieMonster> cookie_monster_;
+  scoped_refptr<base::MessageLoopProxy> cookie_monster_proxy_;
+  base::Lock cookie_monster_lock_;
+
+  // Both these threads are normally NULL. They only exist if CookieManager was
+  // accessed before Chromium was started.
+  scoped_ptr<base::Thread> cookie_monster_client_thread_;
+  scoped_ptr<base::Thread> cookie_monster_backend_thread_;
 
   DISALLOW_COPY_AND_ASSIGN(CookieManager);
 };
@@ -115,27 +176,95 @@ CookieManager::CookieManager() {
 CookieManager::~CookieManager() {
 }
 
-// Executes the |task| on the FILE thread. |wait_for_completion| should only be
-// true if the Java API method returns a value or is explicitly stated to be
-// synchronous.
-void CookieManager::ExecCookieTask(const CookieTask& task,
-                                   const bool wait_for_completion) {
+void CookieManager::CreateCookieMonster(
+    const FilePath& user_data_dir,
+    const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) {
+  FilePath cookie_store_path =
+      user_data_dir.Append(FILE_PATH_LITERAL("Cookies"));
+
+  background_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(ImportLegacyCookieStore, cookie_store_path));
+
+  content::CookieStoreConfig cookie_config(
+      cookie_store_path,
+      content::CookieStoreConfig::RESTORED_SESSION_COOKIES,
+      NULL, NULL);
+  cookie_config.client_task_runner = client_task_runner;
+  cookie_config.background_task_runner = background_task_runner;
+  net::CookieStore* cookie_store = content::CreateCookieStore(cookie_config);
+  cookie_monster_ = cookie_store->GetCookieMonster();
+  SetAcceptFileSchemeCookiesLocked(kDefaultFileSchemeAllowed);
+}
+
+void CookieManager::EnsureCookieMonsterExistsLocked() {
+  cookie_monster_lock_.AssertAcquired();
+  if (cookie_monster_.get()) {
+    return;
+  }
+
+  // Create cookie monster using WebView-specific threads, as the rest of the
+  // browser has not been started yet.
+  FilePath user_data_dir;
+  GetUserDataDir(&user_data_dir);
+  cookie_monster_client_thread_.reset(
+      new base::Thread("CookieMonsterClient"));
+  cookie_monster_client_thread_->Start();
+  cookie_monster_proxy_ = cookie_monster_client_thread_->message_loop_proxy();
+  cookie_monster_backend_thread_.reset(
+      new base::Thread("CookieMonsterBackend"));
+  cookie_monster_backend_thread_->Start();
+
+  CreateCookieMonster(user_data_dir,
+                      cookie_monster_proxy_,
+                      cookie_monster_backend_thread_->message_loop_proxy());
+}
+
+// Executes the |task| on the |cookie_monster_proxy_| message loop.
+void CookieManager::ExecCookieTask(const CookieTask& task) {
   base::WaitableEvent completion(false, false);
+  base::AutoLock lock(cookie_monster_lock_);
 
-  DCHECK(cookie_monster_.get());
+  EnsureCookieMonsterExistsLocked();
 
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-      base::Bind(task, wait_for_completion ? &completion : NULL));
+  cookie_monster_proxy_->PostTask(FROM_HERE, base::Bind(task, &completion));
 
-  if (wait_for_completion) {
-    ScopedAllowWaitForLegacyWebViewApi wait;
-    completion.Wait();
-  }
+  // We always wait for the posted task to complete, even when it doesn't return
+  // a value, because previous versions of the CookieManager API were
+  // synchronous in most/all cases and the caller may be relying on this.
+  ScopedAllowWaitForLegacyWebViewApi wait;
+  completion.Wait();
 }
 
-void CookieManager::SetCookieMonster(net::CookieMonster* cookie_monster) {
+scoped_refptr<net::CookieStore> CookieManager::CreateBrowserThreadCookieStore(
+    AwBrowserContext* browser_context) {
+  base::AutoLock lock(cookie_monster_lock_);
+
+  if (cookie_monster_client_thread_) {
+    // We created a cookie monster already on its own threads; we'll just keep
+    // using it rather than creating one on the normal Chromium threads.
+    // CookieMonster is threadsafe, so this is fine.
+    return cookie_monster_;
+  }
+
+  // Go ahead and create the cookie monster using the normal Chromium threads.
   DCHECK(!cookie_monster_.get());
-  cookie_monster_ = cookie_monster;
+  DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
+
+  FilePath user_data_dir;
+  GetUserDataDir(&user_data_dir);
+  DCHECK(browser_context->GetPath() == user_data_dir);
+
+  cookie_monster_proxy_ =
+      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+      BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
+          BrowserThread::GetBlockingPool()->GetSequenceToken());
+  CreateCookieMonster(user_data_dir,
+                      cookie_monster_proxy_,
+                      background_task_runner);
+  return cookie_monster_;
 }
 
 void CookieManager::SetAcceptCookie(bool accept) {
@@ -146,30 +275,41 @@ bool CookieManager::AcceptCookie() {
   return AwCookieAccessPolicy::GetInstance()->GetGlobalAllowAccess();
 }
 
+void CookieManager::SetAcceptThirdPartyCookie(bool accept) {
+  AwCookieAccessPolicy::GetInstance()->SetThirdPartyAllowAccess(accept);
+}
+
+bool CookieManager::AcceptThirdPartyCookie() {
+  return AwCookieAccessPolicy::GetInstance()->GetThirdPartyAllowAccess();
+}
+
 void CookieManager::SetCookie(const GURL& host,
                               const std::string& cookie_value) {
   ExecCookieTask(base::Bind(&CookieManager::SetCookieAsyncHelper,
                             base::Unretained(this),
                             host,
-                            cookie_value), false);
+                            cookie_value));
 }
 
 void CookieManager::SetCookieAsyncHelper(
     const GURL& host,
     const std::string& value,
     base::WaitableEvent* completion) {
-  DCHECK(!completion);
   net::CookieOptions options;
   options.set_include_httponly();
 
   cookie_monster_->SetCookieWithOptionsAsync(
       host, value, options,
-      base::Bind(&CookieManager::SetCookieCompleted, base::Unretained(this)));
+      base::Bind(&CookieManager::SetCookieCompleted,
+                 base::Unretained(this),
+                 completion));
 }
 
-void CookieManager::SetCookieCompleted(bool success) {
+void CookieManager::SetCookieCompleted(base::WaitableEvent* completion,
+                                       bool success) {
   // The CookieManager API does not return a value for SetCookie,
   // so we don't need to propagate the |success| value back to the caller.
+  completion->Signal();
 }
 
 std::string CookieManager::GetCookie(const GURL& host) {
@@ -177,7 +317,7 @@ std::string CookieManager::GetCookie(const GURL& host) {
   ExecCookieTask(base::Bind(&CookieManager::GetCookieValueAsyncHelper,
                             base::Unretained(this),
                             host,
-                            &cookie_value), true);
+                            &cookie_value));
 
   return cookie_value;
 }
@@ -202,42 +342,41 @@ void CookieManager::GetCookieValueCompleted(base::WaitableEvent* completion,
                                             std::string* result,
                                             const std::string& value) {
   *result = value;
-  DCHECK(completion);
   completion->Signal();
 }
 
 void CookieManager::RemoveSessionCookie() {
   ExecCookieTask(base::Bind(&CookieManager::RemoveSessionCookieAsyncHelper,
-                            base::Unretained(this)), false);
+                            base::Unretained(this)));
 }
 
 void CookieManager::RemoveSessionCookieAsyncHelper(
     base::WaitableEvent* completion) {
-  DCHECK(!completion);
   cookie_monster_->DeleteSessionCookiesAsync(
       base::Bind(&CookieManager::RemoveCookiesCompleted,
-                 base::Unretained(this)));
+                 base::Unretained(this),
+                 completion));
 }
 
-void CookieManager::RemoveCookiesCompleted(int num_deleted) {
+void CookieManager::RemoveCookiesCompleted(base::WaitableEvent* completion,
+                                           int num_deleted) {
   // The CookieManager API does not return a value for removeSessionCookie or
   // removeAllCookie, so we don't need to propagate the |num_deleted| value back
   // to the caller.
+  completion->Signal();
 }
 
 void CookieManager::RemoveAllCookie() {
   ExecCookieTask(base::Bind(&CookieManager::RemoveAllCookieAsyncHelper,
-                            base::Unretained(this)), false);
+                            base::Unretained(this)));
 }
 
-// TODO(kristianm): Pass a null callback so it will not be invoked
-// across threads.
 void CookieManager::RemoveAllCookieAsyncHelper(
     base::WaitableEvent* completion) {
-  DCHECK(!completion);
   cookie_monster_->DeleteAllAsync(
       base::Bind(&CookieManager::RemoveCookiesCompleted,
-                 base::Unretained(this)));
+                 base::Unretained(this),
+                 completion));
 }
 
 void CookieManager::RemoveExpiredCookie() {
@@ -247,20 +386,20 @@ void CookieManager::RemoveExpiredCookie() {
 
 void CookieManager::FlushCookieStoreAsyncHelper(
     base::WaitableEvent* completion) {
-  DCHECK(!completion);
-  cookie_monster_->FlushStore(base::Bind(&base::DoNothing));
+  cookie_monster_->FlushStore(base::Bind(&base::WaitableEvent::Signal,
+                                         base::Unretained(completion)));
 }
 
 void CookieManager::FlushCookieStore() {
   ExecCookieTask(base::Bind(&CookieManager::FlushCookieStoreAsyncHelper,
-                            base::Unretained(this)), false);
+                            base::Unretained(this)));
 }
 
 bool CookieManager::HasCookies() {
   bool has_cookies;
   ExecCookieTask(base::Bind(&CookieManager::HasCookiesAsyncHelper,
                             base::Unretained(this),
-                            &has_cookies), true);
+                            &has_cookies));
   return has_cookies;
 }
 
@@ -279,15 +418,26 @@ void CookieManager::HasCookiesCompleted(base::WaitableEvent* completion,
                                         bool* result,
                                         const CookieList& cookies) {
   *result = cookies.size() != 0;
-  DCHECK(completion);
   completion->Signal();
 }
 
 bool CookieManager::AllowFileSchemeCookies() {
-  return cookie_monster_->IsCookieableScheme(chrome::kFileScheme);
+  base::AutoLock lock(cookie_monster_lock_);
+  EnsureCookieMonsterExistsLocked();
+  return AllowFileSchemeCookiesLocked();
+}
+
+bool CookieManager::AllowFileSchemeCookiesLocked() {
+  return cookie_monster_->IsCookieableScheme(content::kFileScheme);
 }
 
 void CookieManager::SetAcceptFileSchemeCookies(bool accept) {
+  base::AutoLock lock(cookie_monster_lock_);
+  EnsureCookieMonsterExistsLocked();
+  SetAcceptFileSchemeCookiesLocked(accept);
+}
+
+void CookieManager::SetAcceptFileSchemeCookiesLocked(bool accept) {
   // The docs on CookieManager base class state the API must not be called after
   // creating a CookieManager instance (which contradicts its own internal
   // implementation) but this code does rely on the essence of that comment, as
@@ -307,6 +457,16 @@ static jboolean AcceptCookie(JNIEnv* env, jobject obj) {
   return CookieManager::GetInstance()->AcceptCookie();
 }
 
+static void SetAcceptThirdPartyCookie(JNIEnv* env,
+                                      jobject obj,
+                                      jboolean accept) {
+  CookieManager::GetInstance()->SetAcceptThirdPartyCookie(accept);
+}
+
+static jboolean AcceptThirdPartyCookie(JNIEnv* env, jobject obj) {
+  return CookieManager::GetInstance()->AcceptThirdPartyCookie();
+}
+
 static void SetCookie(JNIEnv* env, jobject obj, jstring url, jstring value) {
   GURL host(ConvertJavaStringToUTF16(env, url));
   std::string cookie_value(ConvertJavaStringToUTF8(env, value));
@@ -351,8 +511,10 @@ static void SetAcceptFileSchemeCookies(JNIEnv* env, jobject obj,
   return CookieManager::GetInstance()->SetAcceptFileSchemeCookies(accept);
 }
 
-void SetCookieMonsterOnNetworkStackInit(net::CookieMonster* cookie_monster) {
-  CookieManager::GetInstance()->SetCookieMonster(cookie_monster);
+scoped_refptr<net::CookieStore> CreateCookieStore(
+    AwBrowserContext* browser_context) {
+  return CookieManager::GetInstance()->CreateBrowserThreadCookieStore(
+      browser_context);
 }
 
 bool RegisterCookieManager(JNIEnv* env) {