diff options
author | torne@chromium.org <torne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 15:12:48 +0000 |
---|---|---|
committer | torne@chromium.org <torne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 15:12:48 +0000 |
commit | 23dc0bb182304117b131d5777601140d32aad961 (patch) | |
tree | 7d4c06cceffe4f1657b6dea24156eb6180543e58 /android_webview | |
parent | 8f9f0bffcea02d0889cf4cacce170fcc2450cb71 (diff) | |
download | chromium_src-23dc0bb182304117b131d5777601140d32aad961.zip chromium_src-23dc0bb182304117b131d5777601140d32aad961.tar.gz chromium_src-23dc0bb182304117b131d5777601140d32aad961.tar.bz2 |
Android WebView: Allow CookieManager to be used without starting Chromium.
Refactor the CookieManager to allow it to be used without starting
Chromium. If Chromium has not been started when the cookie manager is
first used, a cookie monster will be created and used to service
requests (with its own client/backend threads). If the app subsequently
triggers regular Chromium startup (e.g. by creating a WebView) then this
existing cookie manager will be used instead of creating one on the
regular Chromium threads. CookieMonster is threadsafe, so this is safe
(it just uses two additional threads).
If apps don't touch CookieManager until after Chromium has been started
for some other reason, then CookieMonster will be created as normal on
the regular Chromium threads.
BUG=304813
Review URL: https://codereview.chromium.org/71583002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237338 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview')
6 files changed, 250 insertions, 75 deletions
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index 2c83ef0..9cf4c6a 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc @@ -10,22 +10,17 @@ #include "android_webview/browser/jni_dependency_factory.h" #include "android_webview/browser/net/aw_url_request_context_getter.h" #include "android_webview/browser/net/init_native_callback.h" -#include "base/android/path_utils.h" -#include "base/file_util.h" -#include "base/files/file_path.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/prefs/pref_service_factory.h" -#include "base/sequenced_task_runner.h" -#include "base/threading/sequenced_worker_pool.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/user_prefs/user_prefs.h" #include "components/visitedlink/browser/visitedlink_master.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/cookie_store_factory.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "net/cookies/cookie_store.h" #include "net/url_request/url_request_context.h" using base::FilePath; @@ -71,27 +66,6 @@ class AwResourceContext : public content::ResourceContext { AwBrowserContext* g_browser_context = NULL; -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/+/bf6f6f9de72c9fd15e6bd/core/java/android/webkit/JniUtil.java and - // https://android.googlesource.com/platform/external/webkit/+/7151ed0c74599/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(); - } -} - } // namespace AwBrowserContext::AwBrowserContext( @@ -127,30 +101,10 @@ AwBrowserContext* AwBrowserContext::FromWebContents( } void AwBrowserContext::PreMainMessageLoopRun() { - - FilePath cookie_store_path = GetPath().Append(FILE_PATH_LITERAL("Cookies")); - scoped_refptr<base::SequencedTaskRunner> background_task_runner = - BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( - BrowserThread::GetBlockingPool()->GetSequenceToken()); - - background_task_runner->PostTask( - FROM_HERE, - base::Bind(ImportLegacyCookieStore, cookie_store_path)); - - cookie_store_ = content::CreatePersistentCookieStore( - cookie_store_path, - true, - NULL, - NULL, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), - background_task_runner); - - cookie_store_->GetCookieMonster()->SetPersistSessionCookies(true); + cookie_store_ = CreateCookieStore(this); url_request_context_getter_ = new AwURLRequestContextGetter(GetPath(), cookie_store_.get()); - DidCreateCookieMonster(cookie_store_->GetCookieMonster()); - visitedlink_master_.reset( new visitedlink::VisitedLinkMaster(this, this, false)); visitedlink_master_->Init(); diff --git a/android_webview/browser/net/init_native_callback.h b/android_webview/browser/net/init_native_callback.h index b2cbec8..5f48e4c 100644 --- a/android_webview/browser/net/init_native_callback.h +++ b/android_webview/browser/net/init_native_callback.h @@ -5,18 +5,20 @@ #ifndef ANDROID_WEBVIEW_BROWSER_NET_INIT_NATIVE_CALLBACK_H_ #define ANDROID_WEBVIEW_BROWSER_NET_INIT_NATIVE_CALLBACK_H_ +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "net/url_request/url_request_job_factory.h" namespace net { -class CookieMonster; +class CookieStore; } // namespace net namespace android_webview { +class AwBrowserContext; -// This is called on the IO thread when the CookieMonster has been created. -// Note that the UI thread is blocked during this call. -void DidCreateCookieMonster(net::CookieMonster* cookie_monster); +// Called when the CookieMonster needs to be created. +scoped_refptr<net::CookieStore> CreateCookieStore( + AwBrowserContext* browser_context); // Called lazily when the job factory is being constructed. scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java new file mode 100644 index 0000000..23636c3 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java @@ -0,0 +1,87 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview.test; + +import android.content.Context; +import android.test.suitebuilder.annotation.MediumTest; + +import org.chromium.android_webview.AwBrowserProcess; +import org.chromium.android_webview.AwContents; +import org.chromium.android_webview.AwCookieManager; +import org.chromium.android_webview.test.util.CommonResources; +import org.chromium.android_webview.test.util.JSUtils; +import org.chromium.base.test.util.Feature; +import org.chromium.content.app.ContentMain; +import org.chromium.net.test.util.TestWebServer; + +/** + * Test for the CookieManager in the case where it's used before Chromium is started. + */ +public class CookieManagerStartupTest extends AwTestBase { + + private AwCookieManager mCookieManager; + private TestAwContentsClient mContentsClient; + private AwContents mAwContents; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mCookieManager = new AwCookieManager(); + assertNotNull(mCookieManager); + + ContentMain.initApplicationContext(getActivity().getApplicationContext()); + } + + @Override + protected boolean needsBrowserProcessStarted() { + return false; + } + + private void startChromium() throws Exception { + final Context context = getActivity(); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + AwBrowserProcess.start(context); + } + }); + + mContentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(mContentsClient); + mAwContents = testContainerView.getAwContents(); + mAwContents.getSettings().setJavaScriptEnabled(true); + } + + @MediumTest + @Feature({"AndroidWebView"}) + public void testStartup() throws Throwable { + TestWebServer webServer = null; + try { + webServer = new TestWebServer(false); + String path = "/cookie_test.html"; + String url = webServer.setResponse(path, CommonResources.ABOUT_HTML, null); + + mCookieManager.setAcceptCookie(true); + mCookieManager.removeAllCookie(); + assertTrue(mCookieManager.acceptCookie()); + assertFalse(mCookieManager.hasCookies()); + mCookieManager.setCookie(url, "count=41"); + + startChromium(); + loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url); + executeJavaScriptAndWaitForResult( + mAwContents, + mContentsClient, + "var c=document.cookie.split('=');document.cookie=c[0]+'='+(1+(+c[1]));"); + + assertEquals("count=42", mCookieManager.getCookie(url)); + } finally { + if (webServer != null) webServer.shutdown(); + } + } + +} diff --git a/android_webview/native/cookie_manager.cc b/android_webview/native/cookie_manager.cc index cd43f30..94e6947 100644 --- a/android_webview/native/cookie_manager.cc +++ b/android_webview/native/cookie_manager.cc @@ -4,25 +4,36 @@ #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_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,11 +54,44 @@ 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(); @@ -97,7 +141,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,16 +174,63 @@ 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::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)); + + net::CookieStore* cookie_store = content::CreatePersistentCookieStore( + cookie_store_path, + true, + NULL, + NULL, + client_task_runner, + background_task_runner); + cookie_monster_ = cookie_store->GetCookieMonster(); + cookie_monster_->SetPersistSessionCookies(true); + 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. +// |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) { base::WaitableEvent completion(false, false); + base::AutoLock lock(cookie_monster_lock_); - DCHECK(cookie_monster_.get()); + EnsureCookieMonsterExistsLocked(); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + cookie_monster_proxy_->PostTask(FROM_HERE, base::Bind(task, wait_for_completion ? &completion : NULL)); if (wait_for_completion) { @@ -133,9 +239,34 @@ void CookieManager::ExecCookieTask(const CookieTask& task, } } -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) { @@ -284,10 +415,22 @@ void CookieManager::HasCookiesCompleted(base::WaitableEvent* completion, } bool CookieManager::AllowFileSchemeCookies() { + base::AutoLock lock(cookie_monster_lock_); + EnsureCookieMonsterExistsLocked(); + return AllowFileSchemeCookiesLocked(); +} + +bool CookieManager::AllowFileSchemeCookiesLocked() { return cookie_monster_->IsCookieableScheme(chrome::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 @@ -351,8 +494,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) { diff --git a/android_webview/native/cookie_manager.h b/android_webview/native/cookie_manager.h index d1e0522..2807555 100644 --- a/android_webview/native/cookie_manager.h +++ b/android_webview/native/cookie_manager.h @@ -7,14 +7,7 @@ #include <jni.h> -namespace net { -class CookieMonster; -} // namespace net - namespace android_webview { -class AwURLRequestJobFactory; - -void SetCookieMonsterOnNetworkStackInit(net::CookieMonster* cookie_monster); bool RegisterCookieManager(JNIEnv* env); diff --git a/android_webview/native/net_init_native_callback.cc b/android_webview/native/net_init_native_callback.cc index d291dfd..f355c03 100644 --- a/android_webview/native/net_init_native_callback.cc +++ b/android_webview/native/net_init_native_callback.cc @@ -6,17 +6,11 @@ #include "android_webview/browser/net/aw_url_request_job_factory.h" #include "android_webview/native/android_protocol_handler.h" -#include "android_webview/native/cookie_manager.h" #include "base/logging.h" namespace android_webview { class AwURLRequestJobFactory; -void DidCreateCookieMonster(net::CookieMonster* cookie_monster) { - DCHECK(cookie_monster); - SetCookieMonsterOnNetworkStackInit(cookie_monster); -} - scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> CreateAndroidAssetFileProtocolHandler() { return CreateAssetFileProtocolHandler(); |