summaryrefslogtreecommitdiffstats
path: root/android_webview
diff options
context:
space:
mode:
Diffstat (limited to 'android_webview')
-rw-r--r--android_webview/DEPS1
-rw-r--r--android_webview/android_webview.gyp5
-rw-r--r--android_webview/browser/aw_cookie_access_policy.cc71
-rw-r--r--android_webview/browser/aw_cookie_access_policy.h73
-rw-r--r--android_webview/browser/net/aw_network_delegate.cc109
-rw-r--r--android_webview/browser/net/aw_network_delegate.h69
-rwxr-xr-xandroid_webview/build/install_binary5
-rw-r--r--android_webview/java/src/org/chromium/android_webview/CookieManager.java190
-rwxr-xr-xandroid_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java246
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/TestWebServer.java431
-rw-r--r--android_webview/lib/aw_browser_dependency_factory_impl.cc50
-rw-r--r--android_webview/lib/aw_browser_dependency_factory_impl.h18
-rw-r--r--android_webview/lib/aw_content_browser_client.cc35
-rw-r--r--android_webview/lib/aw_content_browser_client.h13
-rw-r--r--android_webview/native/android_webview_jni_registrar.cc2
-rw-r--r--android_webview/native/aw_browser_dependency_factory.h4
-rw-r--r--android_webview/native/cookie_manager.cc303
-rw-r--r--android_webview/native/cookie_manager.h16
-rw-r--r--android_webview/native/webview_native.gyp3
19 files changed, 1638 insertions, 6 deletions
diff --git a/android_webview/DEPS b/android_webview/DEPS
index 514d673d..0a18ac3 100644
--- a/android_webview/DEPS
+++ b/android_webview/DEPS
@@ -13,4 +13,5 @@ include_rules = [
"+chrome/browser/component",
"+content/public",
"+jni",
+ "+net",
]
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp
index 1f68dda..42fd686 100644
--- a/android_webview/android_webview.gyp
+++ b/android_webview/android_webview.gyp
@@ -26,10 +26,14 @@
'common/android_webview_message_generator.h',
'common/render_view_messages.cc',
'common/render_view_messages.h',
+ 'browser/net/aw_network_delegate.cc',
+ 'browser/net/aw_network_delegate.h',
'browser/renderer_host/aw_render_view_host_ext.cc',
'browser/renderer_host/aw_render_view_host_ext.h',
'browser/renderer_host/aw_resource_dispatcher_host_delegate.cc',
'browser/renderer_host/aw_resource_dispatcher_host_delegate.h',
+ 'browser/aw_cookie_access_policy.cc',
+ 'browser/aw_cookie_access_policy.h',
'lib/aw_browser_dependency_factory_impl.cc',
'lib/aw_browser_dependency_factory_impl.h',
'lib/aw_content_browser_client.cc',
@@ -61,6 +65,7 @@
'<(android_product_out)/obj/lib/libwebview.so',
'<(android_product_out)/system/lib/libwebview.so',
'<(android_product_out)/symbols/system/lib/libwebview.so',
+ '<(android_product_out)/symbols/data/data/org.chromium.android_webview/lib/libwebview.so',
],
'action': [
'<(install_binary_script)',
diff --git a/android_webview/browser/aw_cookie_access_policy.cc b/android_webview/browser/aw_cookie_access_policy.cc
new file mode 100644
index 0000000..4809fdb
--- /dev/null
+++ b/android_webview/browser/aw_cookie_access_policy.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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.
+
+#include "android_webview/browser/aw_cookie_access_policy.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/browser_thread.h"
+
+using base::AutoLock;
+using content::BrowserThread;
+
+namespace android_webview {
+
+namespace {
+base::LazyInstance<AwCookieAccessPolicy>::Leaky g_lazy_instance;
+} // namespace
+
+AwCookieAccessPolicy::~AwCookieAccessPolicy() {
+}
+
+AwCookieAccessPolicy::AwCookieAccessPolicy()
+ : allow_access_(false) {
+}
+
+AwCookieAccessPolicy* AwCookieAccessPolicy::GetInstance() {
+ return g_lazy_instance.Pointer();
+}
+
+bool AwCookieAccessPolicy::GetGlobalAllowAccess() {
+ AutoLock lock(lock_);
+ return allow_access_;
+}
+
+void AwCookieAccessPolicy::SetGlobalAllowAccess(bool allow) {
+ AutoLock lock(lock_);
+ allow_access_ = allow;
+}
+
+bool AwCookieAccessPolicy::OnCanGetCookies(const net::URLRequest& request,
+ const net::CookieList& cookie_list) {
+ return GetGlobalAllowAccess();
+}
+
+bool AwCookieAccessPolicy::OnCanSetCookie(const net::URLRequest& request,
+ const std::string& cookie_line,
+ net::CookieOptions* options) {
+ return GetGlobalAllowAccess();
+}
+
+bool AwCookieAccessPolicy::AllowGetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id) {
+ return GetGlobalAllowAccess();
+}
+
+bool AwCookieAccessPolicy::AllowSetCookie(const GURL& url,
+ const GURL& first_party,
+ const std::string& cookie_line,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id,
+ net::CookieOptions* options) {
+ return GetGlobalAllowAccess();
+}
+
+} // namespace android_webview
diff --git a/android_webview/browser/aw_cookie_access_policy.h b/android_webview/browser/aw_cookie_access_policy.h
new file mode 100644
index 0000000..b25e4f6
--- /dev/null
+++ b/android_webview/browser/aw_cookie_access_policy.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_AW_COOKIE_ACCESS_POLICY_H_
+#define ANDROID_WEBVIEW_BROWSER_AW_COOKIE_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "net/cookies/canonical_cookie.h"
+
+namespace content {
+class ResourceContext;
+}
+
+namespace net {
+class CookieOptions;
+class URLRequest;
+}
+
+class GURL;
+
+namespace android_webview {
+
+// Manages the cookie access (both setting and getting) policy for WebView.
+class AwCookieAccessPolicy {
+ public:
+ static AwCookieAccessPolicy* GetInstance();
+
+ // These manage the global access state shared across requests regardless of
+ // source (i.e. network or JavaScript).
+ bool GetGlobalAllowAccess();
+ void SetGlobalAllowAccess(bool allow);
+
+ // These are the functions called when operating over cookies from the
+ // network. See NetworkDelegate for further descriptions.
+ bool OnCanGetCookies(const net::URLRequest& request,
+ const net::CookieList& cookie_list);
+ bool OnCanSetCookie(const net::URLRequest& request,
+ const std::string& cookie_line,
+ net::CookieOptions* options);
+
+ // These are the functions called when operating over cookies from the
+ // renderer. See ContentBrowserClient for further descriptions.
+ bool AllowGetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id);
+ bool AllowSetCookie(const GURL& url,
+ const GURL& first_party,
+ const std::string& cookie_line,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id,
+ net::CookieOptions* options);
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<AwCookieAccessPolicy>;
+
+ AwCookieAccessPolicy();
+ ~AwCookieAccessPolicy();
+ bool allow_access_;
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(AwCookieAccessPolicy);
+};
+
+} // namespace android_webview
+
+#endif // ANDROID_WEBVIEW_BROWSER_AW_COOKIE_ACCESS_POLICY_H_
diff --git a/android_webview/browser/net/aw_network_delegate.cc b/android_webview/browser/net/aw_network_delegate.cc
new file mode 100644
index 0000000..0cbb2e4
--- /dev/null
+++ b/android_webview/browser/net/aw_network_delegate.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 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.
+
+#include "android_webview/browser/net/aw_network_delegate.h"
+
+#include "android_webview/browser/aw_cookie_access_policy.h"
+#include "net/base/net_errors.h"
+#include "net/base/completion_callback.h"
+#include "net/url_request/url_request.h"
+
+namespace android_webview {
+
+AwNetworkDelegate::AwNetworkDelegate() {
+}
+
+AwNetworkDelegate::~AwNetworkDelegate() {
+}
+
+int AwNetworkDelegate::OnBeforeURLRequest(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ GURL* new_url) {
+ return net::OK;
+}
+
+int AwNetworkDelegate::OnBeforeSendHeaders(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ net::HttpRequestHeaders* headers) {
+ return net::OK;
+}
+
+void AwNetworkDelegate::OnSendHeaders(net::URLRequest* request,
+ const net::HttpRequestHeaders& headers) {
+}
+
+int AwNetworkDelegate::OnHeadersReceived(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
+ return net::OK;
+}
+
+void AwNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
+ const GURL& new_location) {
+}
+
+void AwNetworkDelegate::OnResponseStarted(net::URLRequest* request) {
+}
+
+void AwNetworkDelegate::OnRawBytesRead(const net::URLRequest& request,
+ int bytes_read) {
+}
+
+void AwNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) {
+}
+
+void AwNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) {
+}
+
+void AwNetworkDelegate::OnPACScriptError(int line_number,
+ const string16& error) {
+}
+
+net::NetworkDelegate::AuthRequiredResponse AwNetworkDelegate::OnAuthRequired(
+ net::URLRequest* request,
+ const net::AuthChallengeInfo& auth_info,
+ const AuthCallback& callback,
+ net::AuthCredentials* credentials) {
+ return AUTH_REQUIRED_RESPONSE_NO_ACTION;
+}
+
+bool AwNetworkDelegate::OnCanGetCookies(const net::URLRequest& request,
+ const net::CookieList& cookie_list) {
+ return AwCookieAccessPolicy::GetInstance()->OnCanGetCookies(request,
+ cookie_list);
+}
+
+bool AwNetworkDelegate::OnCanSetCookie(const net::URLRequest& request,
+ const std::string& cookie_line,
+ net::CookieOptions* options) {
+ return AwCookieAccessPolicy::GetInstance()->OnCanSetCookie(request,
+ cookie_line,
+ options);
+}
+
+bool AwNetworkDelegate::OnCanAccessFile(const net::URLRequest& request,
+ const FilePath& path) const {
+ return true;
+}
+
+bool AwNetworkDelegate::OnCanThrottleRequest(
+ const net::URLRequest& request) const {
+ return false;
+}
+
+int AwNetworkDelegate::OnBeforeSocketStreamConnect(
+ net::SocketStream* stream,
+ const net::CompletionCallback& callback) {
+ return net::OK;
+}
+
+void AwNetworkDelegate::OnRequestWaitStateChange(const net::URLRequest& request,
+ RequestWaitState state) {
+}
+
+} // namespace android_webview
diff --git a/android_webview/browser/net/aw_network_delegate.h b/android_webview/browser/net/aw_network_delegate.h
new file mode 100644
index 0000000..c4505f9
--- /dev/null
+++ b/android_webview/browser/net/aw_network_delegate.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 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.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_NET_AW_NETWORK_DELEGATE_H_
+#define ANDROID_WEBVIEW_BROWSER_NET_AW_NETWORK_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "net/base/network_delegate.h"
+
+namespace android_webview {
+
+// WebView's implementation of the NetworkDelegate.
+class AwNetworkDelegate : public net::NetworkDelegate {
+ public:
+ AwNetworkDelegate();
+ virtual ~AwNetworkDelegate();
+
+ private:
+ // NetworkDelegate implementation.
+ virtual int OnBeforeURLRequest(net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ GURL* new_url) OVERRIDE;
+ virtual int OnBeforeSendHeaders(net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ net::HttpRequestHeaders* headers) OVERRIDE;
+ virtual void OnSendHeaders(net::URLRequest* request,
+ const net::HttpRequestHeaders& headers) OVERRIDE;
+ virtual int OnHeadersReceived(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers)
+ OVERRIDE;
+ virtual void OnBeforeRedirect(net::URLRequest* request,
+ const GURL& new_location) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnRawBytesRead(const net::URLRequest& request,
+ int bytes_read) OVERRIDE;
+ virtual void OnCompleted(net::URLRequest* request, bool started) OVERRIDE;
+ virtual void OnURLRequestDestroyed(net::URLRequest* request) OVERRIDE;
+ virtual void OnPACScriptError(int line_number,
+ const string16& error) OVERRIDE;
+ virtual net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(
+ net::URLRequest* request,
+ const net::AuthChallengeInfo& auth_info,
+ const AuthCallback& callback,
+ net::AuthCredentials* credentials) OVERRIDE;
+ virtual bool OnCanGetCookies(const net::URLRequest& request,
+ const net::CookieList& cookie_list) OVERRIDE;
+ virtual bool OnCanSetCookie(const net::URLRequest& request,
+ const std::string& cookie_line,
+ net::CookieOptions* options) OVERRIDE;
+ virtual bool OnCanAccessFile(const net::URLRequest& request,
+ const FilePath& path) const OVERRIDE;
+ virtual bool OnCanThrottleRequest(
+ const net::URLRequest& request) const OVERRIDE;
+ virtual int OnBeforeSocketStreamConnect(
+ net::SocketStream* stream,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual void OnRequestWaitStateChange(const net::URLRequest& request,
+ RequestWaitState state) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(AwNetworkDelegate);
+};
+
+} // namespace android_webview
+
+#endif // ANDROID_WEBVIEW_BROWSER_NET_AW_NETWORK_DELEGATE_H_
diff --git a/android_webview/build/install_binary b/android_webview/build/install_binary
index 24aaa83..ad63e0d 100755
--- a/android_webview/build/install_binary
+++ b/android_webview/build/install_binary
@@ -7,7 +7,7 @@
if [ "$3" = "" ]
then
- echo "Usage: install_binary path/to/binary path/to/target1 path/to/target2 path/to/symbols"
+ echo "Usage: install_binary path/to/binary path/to/target1 path/to/target2 path/to/symbols path/to/symbols2"
exit 1
fi
@@ -15,9 +15,12 @@ SOURCE=$1
TARGET=$2
TARGET2=$3
SYMBOLS=$4
+SYMBOLS2=$5
mkdir -p $(dirname $SYMBOLS)
cp $SOURCE $SYMBOLS
+# Create a hard link to avoid the additional copy to the secondary location.
+ln $SYMBOLS $SYMBOLS2
$STRIP --strip-unneeded $SOURCE -o $TARGET
cp $TARGET $TARGET2
diff --git a/android_webview/java/src/org/chromium/android_webview/CookieManager.java b/android_webview/java/src/org/chromium/android_webview/CookieManager.java
new file mode 100644
index 0000000..038baa3
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/CookieManager.java
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 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;
+
+import android.net.ParseException;
+import android.util.Log;
+
+import org.chromium.base.JNINamespace;
+import org.chromium.base.ThreadUtils;
+
+import java.util.concurrent.Callable;
+
+/**
+ * CookieManager manages cookies according to RFC2109 spec.
+ *
+ * Methods in this class are thread safe.
+ */
+@JNINamespace("android_webview")
+public final class CookieManager {
+ private static final String LOGTAG = "CookieManager";
+
+ /**
+ * Control whether cookie is enabled or disabled
+ * @param accept TRUE if accept cookie
+ */
+ public synchronized void setAcceptCookie(boolean accept) {
+ final boolean finalAccept = accept;
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ nativeSetAcceptCookie(finalAccept);
+ }
+ });
+ }
+
+ private final Callable<Boolean> acceptCookieCallable = new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return nativeAcceptCookie();
+ }
+ };
+
+ /**
+ * Return whether cookie is enabled
+ * @return TRUE if accept cookie
+ */
+ public synchronized boolean acceptCookie() {
+ return ThreadUtils.runOnUiThreadBlockingNoException(acceptCookieCallable);
+ }
+
+ /**
+ * Set cookie for a given url. The old cookie with same host/path/name will
+ * be removed. The new cookie will be added if it is not expired or it does
+ * not have expiration which implies it is session cookie.
+ * @param url The url which cookie is set for
+ * @param value The value for set-cookie: in http response header
+ */
+ public void setCookie(final String url, final String value) {
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ nativeSetCookie(url, value);
+ }
+ });
+ }
+
+ /**
+ * Get cookie(s) for a given url so that it can be set to "cookie:" in http
+ * request header.
+ * @param url The url needs cookie
+ * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
+ */
+ public String getCookie(final String url) {
+ String cookie = ThreadUtils.runOnUiThreadBlockingNoException(new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return nativeGetCookie(url.toString());
+ }
+ });
+ // Return null if the string is empty to match legacy behavior
+ return cookie == null || cookie.trim().isEmpty() ? null : cookie;
+ }
+
+ private final Runnable removeSessionCookieRunnable = new Runnable() {
+ @Override
+ public void run() {
+ nativeRemoveSessionCookie();
+ }
+ };
+
+ /**
+ * Remove all session cookies, which are cookies without expiration date
+ */
+ public void removeSessionCookie() {
+ ThreadUtils.runOnUiThread(removeSessionCookieRunnable);
+ }
+
+ private final Runnable removeAllCookieRunnable = new Runnable() {
+ @Override
+ public void run() {
+ nativeRemoveAllCookie();
+ }
+ };
+
+ /**
+ * Remove all cookies
+ */
+ public void removeAllCookie() {
+ ThreadUtils.runOnUiThread(removeAllCookieRunnable);
+ }
+
+ private final Callable<Boolean> hasCookiesCallable = new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return nativeHasCookies();
+ }
+ };
+
+ /**
+ * Return true if there are stored cookies.
+ */
+ public synchronized boolean hasCookies() {
+ return ThreadUtils.runOnUiThreadBlockingNoException(hasCookiesCallable);
+ }
+
+ private final Runnable removeExpiredCookieRunnable = new Runnable() {
+ @Override
+ public void run() {
+ nativeRemoveExpiredCookie();
+ }
+ };
+
+ /**
+ * Remove all expired cookies
+ */
+ public void removeExpiredCookie() {
+ ThreadUtils.runOnUiThread(removeExpiredCookieRunnable);
+ }
+
+ private static final Callable<Boolean> allowFileSchemeCookiesCallable =
+ new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return nativeAllowFileSchemeCookies();
+ }
+ };
+
+ /**
+ * Whether cookies are accepted for file scheme URLs.
+ */
+ public static boolean allowFileSchemeCookies() {
+ return ThreadUtils.runOnUiThreadBlockingNoException(allowFileSchemeCookiesCallable);
+ }
+
+ /**
+ * Sets whether cookies are accepted for file scheme URLs.
+ *
+ * Use of cookies with file scheme URLs is potentially insecure. Do not use this feature unless
+ * you can be sure that no unintentional sharing of cookie data can take place.
+ * <p>
+ * Note that calls to this method will have no effect if made after a WebView or CookieManager
+ * instance has been created.
+ */
+ public static void setAcceptFileSchemeCookies(boolean accept) {
+ final boolean finalAccept = accept;
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ nativeSetAcceptFileSchemeCookies(finalAccept);
+ }
+ });
+ }
+
+ private native void nativeSetAcceptCookie(boolean accept);
+ private native boolean nativeAcceptCookie();
+
+ private native void nativeSetCookie(String url, String value);
+ private native String nativeGetCookie(String url);
+
+ private native void nativeRemoveSessionCookie();
+ private native void nativeRemoveAllCookie();
+ private native void nativeRemoveExpiredCookie();
+
+ private native boolean nativeHasCookies();
+
+ static native boolean nativeAllowFileSchemeCookies();
+ static native void nativeSetAcceptFileSchemeCookies(boolean accept);
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
new file mode 100755
index 0000000..c2a5868
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 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.test.MoreAsserts;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import org.chromium.android_webview.CookieManager;
+import org.chromium.base.test.Feature;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.Criteria;
+import org.chromium.content.browser.test.CriteriaHelper;
+import org.chromium.content.browser.test.TestContentViewClient.OnEvaluateJavaScriptResultHelper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Tests for the CookieManager.
+ */
+public class CookieManagerTest extends AndroidWebViewTestBase {
+
+ private CookieManager mCookieManager;
+ private TestAwContentsClient mContentViewClient;
+ private ContentViewCore mContentViewCore;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mCookieManager = new CookieManager();
+ mContentViewClient = new TestAwContentsClient();
+ mContentViewCore =
+ createAwTestContainerViewOnMainSync(mContentViewClient).getContentViewCore();
+ mContentViewCore.getContentSettings().setJavaScriptEnabled(true);
+ assertNotNull(mCookieManager);
+ }
+
+ @SmallTest
+ @Feature({"Android-WebView", "Privacy"})
+ public void testAllowFileSchemeCookies() throws Throwable {
+ assertFalse(CookieManager.allowFileSchemeCookies());
+ CookieManager.setAcceptFileSchemeCookies(true);
+ assertTrue(CookieManager.allowFileSchemeCookies());
+ CookieManager.setAcceptFileSchemeCookies(false);
+ assertFalse(CookieManager.allowFileSchemeCookies());
+ }
+
+ @MediumTest
+ @Feature({"Android-WebView", "Privacy"})
+ public void testAcceptCookie() throws Throwable {
+ TestWebServer webServer = null;
+ try {
+ webServer = new TestWebServer(false);
+ String path = "/cookie_test.html";
+ String responseStr =
+ "<html><head><title>TEST!</title></head><body>HELLO!</body></html>";
+ String url = webServer.setResponse(path, responseStr, null);
+
+ mCookieManager.setAcceptCookie(false);
+ mCookieManager.removeAllCookie();
+ assertFalse(mCookieManager.acceptCookie());
+ assertFalse(mCookieManager.hasCookies());
+
+ loadUrlSync(mContentViewCore, mContentViewClient.getOnPageFinishedHelper(), url);
+ setCookie("test1", "value1");
+ assertNull(mCookieManager.getCookie(url));
+
+ List<Pair<String, String>> responseHeaders = new ArrayList<Pair<String, String>>();
+ responseHeaders.add(
+ Pair.create("Set-Cookie", "header-test1=header-value1; path=" + path));
+ url = webServer.setResponse(path, responseStr, responseHeaders);
+ loadUrlSync(mContentViewCore, mContentViewClient.getOnPageFinishedHelper(), url);
+ assertNull(mCookieManager.getCookie(url));
+
+ mCookieManager.setAcceptCookie(true);
+ assertTrue(mCookieManager.acceptCookie());
+
+ url = webServer.setResponse(path, responseStr, null);
+ loadUrlSync(mContentViewCore, mContentViewClient.getOnPageFinishedHelper(), url);
+ setCookie("test2", "value2");
+ waitForCookie(url);
+ String cookie = mCookieManager.getCookie(url);
+ assertNotNull(cookie);
+ validateCookies(cookie, "test2");
+
+ responseHeaders = new ArrayList<Pair<String, String>>();
+ responseHeaders.add(
+ Pair.create("Set-Cookie", "header-test2=header-value2 path=" + path));
+ url = webServer.setResponse(path, responseStr, responseHeaders);
+ loadUrlSync(mContentViewCore, mContentViewClient.getOnPageFinishedHelper(), url);
+ waitForCookie(url);
+ cookie = mCookieManager.getCookie(url);
+ assertNotNull(cookie);
+ validateCookies(cookie, "test2", "header-test2");
+
+ // clean up all cookies
+ mCookieManager.removeAllCookie();
+ } finally {
+ if (webServer != null) webServer.shutdown();
+ }
+ }
+
+ private void setCookie(final String name, final String value)
+ throws Throwable {
+ OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper =
+ mContentViewClient.getOnEvaluateJavaScriptResultHelper();
+ int currentCallCount = onEvaluateJavaScriptResultHelper.getCallCount();
+ final AtomicInteger requestId = new AtomicInteger();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ requestId.set(mContentViewCore.evaluateJavaScript(
+ "var expirationDate = new Date();" +
+ "expirationDate.setDate(expirationDate.getDate() + 5);" +
+ "document.cookie='" + name + "=" + value +
+ "; expires=' + expirationDate.toUTCString();"));
+ }
+ });
+ onEvaluateJavaScriptResultHelper.waitForCallback(currentCallCount);
+ assertEquals("Response ID mismatch when evaluating JavaScript.",
+ requestId.get(), onEvaluateJavaScriptResultHelper.getId());
+ }
+
+ private void waitForCookie(final String url) throws InterruptedException {
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCookieManager.getCookie(url) != null;
+ }
+ }));
+ }
+
+ private void validateCookies(String responseCookie, String... expectedCookieNames) {
+ String[] cookies = responseCookie.split(";");
+ Set<String> foundCookieNames = new HashSet<String>();
+ for (String cookie : cookies) {
+ foundCookieNames.add(cookie.substring(0, cookie.indexOf("=")).trim());
+ }
+ MoreAsserts.assertEquals(
+ foundCookieNames, new HashSet<String>(Arrays.asList(expectedCookieNames)));
+ }
+
+ @MediumTest
+ @Feature({"Android-WebView", "Privacy"})
+ public void testRemoveAllCookie() throws InterruptedException {
+ // enable cookie
+ mCookieManager.setAcceptCookie(true);
+ assertTrue(mCookieManager.acceptCookie());
+
+ // first there should be no cookie stored
+ mCookieManager.removeAllCookie();
+ assertFalse(mCookieManager.hasCookies());
+
+ String url = "http://www.example.com";
+ String cookie = "name=test";
+ mCookieManager.setCookie(url, cookie);
+ assertEquals(cookie, mCookieManager.getCookie(url));
+
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCookieManager.hasCookies();
+ }
+ }));
+
+ // clean up all cookies
+ mCookieManager.removeAllCookie();
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return !mCookieManager.hasCookies();
+ }
+ }));
+ }
+
+ @MediumTest
+ @Feature({"Android-WebView", "Privacy"})
+ @SuppressWarnings("deprecation")
+ public void testCookieExpiration() throws InterruptedException {
+ // enable cookie
+ mCookieManager.setAcceptCookie(true);
+ assertTrue(mCookieManager.acceptCookie());
+ mCookieManager.removeAllCookie();
+ assertFalse(mCookieManager.hasCookies());
+
+ final String url = "http://www.example.com";
+ final String cookie1 = "cookie1=peter";
+ final String cookie2 = "cookie2=sue";
+ final String cookie3 = "cookie3=marc";
+
+ mCookieManager.setCookie(url, cookie1); // session cookie
+
+ Date date = new Date();
+ date.setTime(date.getTime() + 1000 * 600);
+ String value2 = cookie2 + "; expires=" + date.toGMTString();
+ mCookieManager.setCookie(url, value2); // expires in 10min
+
+ long expiration = 3000;
+ date = new Date();
+ date.setTime(date.getTime() + expiration);
+ String value3 = cookie3 + "; expires=" + date.toGMTString();
+ mCookieManager.setCookie(url, value3); // expires in 3s
+
+ String allCookies = mCookieManager.getCookie(url);
+ assertTrue(allCookies.contains(cookie1));
+ assertTrue(allCookies.contains(cookie2));
+ assertTrue(allCookies.contains(cookie3));
+
+ mCookieManager.removeSessionCookie();
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ String c = mCookieManager.getCookie(url);
+ return !c.contains(cookie1) && c.contains(cookie2) && c.contains(cookie3);
+ }
+ }));
+
+ Thread.sleep(expiration + 1000); // wait for cookie to expire
+ mCookieManager.removeExpiredCookie();
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ String c = mCookieManager.getCookie(url);
+ return !c.contains(cookie1) && c.contains(cookie2) && !c.contains(cookie3);
+ }
+ }));
+
+ mCookieManager.removeAllCookie();
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCookieManager.getCookie(url) == null;
+ }
+ }));
+ }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestWebServer.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestWebServer.java
new file mode 100644
index 0000000..8508a51
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestWebServer.java
@@ -0,0 +1,431 @@
+// Copyright (c) 2012 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.util.Base64;
+import android.util.Log;
+import android.util.Pair;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Simple http test server for testing.
+ *
+ * Based heavily on the CTSWebServer in Android.
+ */
+public class TestWebServer {
+ private static final String TAG = "TestWebServer";
+ private static final int SERVER_PORT = 4444;
+ private static final int SSL_SERVER_PORT = 4445;
+
+ public static final String SHUTDOWN_PREFIX = "/shutdown";
+
+ private static TestWebServer sInstance;
+ private static Hashtable<Integer, String> sReasons;
+
+ private ServerThread mServerThread;
+ private String mServerUri;
+ private boolean mSsl;
+
+ private static class Response {
+ final String mResponseStr;
+ final List<Pair<String, String>> mResponseHeaders;
+
+ Response(String responseStr, List<Pair<String, String>> responseHeaders) {
+ mResponseStr = responseStr;
+ mResponseHeaders = responseHeaders == null ?
+ new ArrayList<Pair<String, String>>() : responseHeaders;
+ }
+ }
+
+ private Map<String, Response> mResponseMap = new HashMap<String, Response>();
+
+ /**
+ * Create and start a local HTTP server instance.
+ * @param ssl True if the server should be using secure sockets.
+ * @throws Exception
+ */
+ public TestWebServer(boolean ssl) throws Exception {
+ if (sInstance != null) {
+ // attempt to start a new instance while one is still running
+ // shut down the old instance first
+ sInstance.shutdown();
+ }
+ sInstance = this;
+ mSsl = ssl;
+ if (mSsl) {
+ mServerUri = "https://localhost:" + SSL_SERVER_PORT;
+ } else {
+ mServerUri = "http://localhost:" + SERVER_PORT;
+ }
+ mServerThread = new ServerThread(this, mSsl);
+ mServerThread.start();
+ }
+
+ /**
+ * Terminate the http server.
+ */
+ public void shutdown() {
+ try {
+ // Avoid a deadlock between two threads where one is trying to call
+ // close() and the other one is calling accept() by sending a GET
+ // request for shutdown and having the server's one thread
+ // sequentially call accept() and close().
+ URL url = new URL(mServerUri + SHUTDOWN_PREFIX);
+ URLConnection connection = openConnection(url);
+ connection.connect();
+
+ // Read the input from the stream to send the request.
+ InputStream is = connection.getInputStream();
+ is.close();
+
+ // Block until the server thread is done shutting down.
+ mServerThread.join();
+
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ } catch (KeyManagementException e) {
+ throw new IllegalStateException(e);
+ }
+
+ sInstance = null;
+ }
+
+ /**
+ * Sets a response to be returned when a particular request path is passed
+ * in (with the option to specify additional headers).
+ *
+ * @param requestPath The path to respond to.
+ * @param resposneString The response body that will be returned.
+ * @param responseHeaders Any additional headers that should be returned along with the
+ * response (null is acceptable).
+ * @return The full URL including the path that should be requested to get the expected
+ * response.
+ */
+ public String setResponse(
+ String requestPath, String resposneString,
+ List<Pair<String, String>> responseHeaders) {
+ mResponseMap.put(requestPath, new Response(resposneString, responseHeaders));
+ return mServerUri + requestPath;
+ }
+
+ private URLConnection openConnection(URL url)
+ throws IOException, NoSuchAlgorithmException, KeyManagementException {
+ if (mSsl) {
+ // Install hostname verifiers and trust managers that don't do
+ // anything in order to get around the client not trusting
+ // the test server due to a lack of certificates.
+
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setHostnameVerifier(new TestHostnameVerifier());
+
+ SSLContext context = SSLContext.getInstance("TLS");
+ TestTrustManager trustManager = new TestTrustManager();
+ context.init(null, new TestTrustManager[] {trustManager}, null);
+ connection.setSSLSocketFactory(context.getSocketFactory());
+
+ return connection;
+ } else {
+ return url.openConnection();
+ }
+ }
+
+ /**
+ * {@link X509TrustManager} that trusts everybody. This is used so that
+ * the client calling {@link TestWebServer#shutdown()} can issue a request
+ * for shutdown by blindly trusting the {@link TestWebServer}'s
+ * credentials.
+ */
+ private static class TestTrustManager implements X509TrustManager {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ // Trust the TestWebServer...
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ // Trust the TestWebServer...
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+
+ /**
+ * {@link HostnameVerifier} that verifies everybody. This permits
+ * the client to trust the web server and call
+ * {@link TestWebServer#shutdown()}.
+ */
+ private static class TestHostnameVerifier implements HostnameVerifier {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ }
+
+ /**
+ * Generate a response to the given request.
+ * @throws InterruptedException
+ */
+ private HttpResponse getResponse(HttpRequest request) throws InterruptedException {
+ RequestLine requestLine = request.getRequestLine();
+ HttpResponse httpResponse = null;
+ Log.i(TAG, requestLine.getMethod() + ": " + requestLine.getUri());
+ String uriString = requestLine.getUri();
+ URI uri = URI.create(uriString);
+ String path = uri.getPath();
+
+ Response response = mResponseMap.get(path);
+ if (path.equals(SHUTDOWN_PREFIX)) {
+ httpResponse = createResponse(HttpStatus.SC_OK);
+ } else if (response == null) {
+ httpResponse = createResponse(HttpStatus.SC_NOT_FOUND);
+ } else {
+ httpResponse = createResponse(HttpStatus.SC_OK);
+ httpResponse.setEntity(createEntity(response.mResponseStr));
+ for (Pair<String, String> header : response.mResponseHeaders) {
+ httpResponse.addHeader(header.first, header.second);
+ }
+ }
+ StatusLine sl = httpResponse.getStatusLine();
+ Log.i(TAG, sl.getStatusCode() + "(" + sl.getReasonPhrase() + ")");
+ setDateHeaders(httpResponse);
+ return httpResponse;
+ }
+
+ private void setDateHeaders(HttpResponse response) {
+ long time = System.currentTimeMillis();
+ response.addHeader("Date", DateUtils.formatDate(new Date(), DateUtils.PATTERN_RFC1123));
+ }
+
+ /**
+ * Create an empty response with the given status.
+ */
+ private HttpResponse createResponse(int status) {
+ HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, null);
+
+ if (sReasons == null) {
+ sReasons = new Hashtable<Integer, String>();
+ sReasons.put(HttpStatus.SC_UNAUTHORIZED, "Unauthorized");
+ sReasons.put(HttpStatus.SC_NOT_FOUND, "Not Found");
+ sReasons.put(HttpStatus.SC_FORBIDDEN, "Forbidden");
+ sReasons.put(HttpStatus.SC_MOVED_TEMPORARILY, "Moved Temporarily");
+ }
+ // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is Locale-dependent.
+ String reason = sReasons.get(status);
+
+ if (reason != null) {
+ StringBuffer buf = new StringBuffer("<html><head><title>");
+ buf.append(reason);
+ buf.append("</title></head><body>");
+ buf.append(reason);
+ buf.append("</body></html>");
+ response.setEntity(createEntity(buf.toString()));
+ }
+ return response;
+ }
+
+ /**
+ * Create a string entity for the given content.
+ */
+ private StringEntity createEntity(String content) {
+ try {
+ StringEntity entity = new StringEntity(content);
+ entity.setContentType("text/html");
+ return entity;
+ } catch (UnsupportedEncodingException e) {
+ Log.w(TAG, e);
+ }
+ return null;
+ }
+
+ private static class ServerThread extends Thread {
+ private TestWebServer mServer;
+ private ServerSocket mSocket;
+ private boolean mIsSsl;
+ private boolean mIsCancelled;
+ private SSLContext mSslContext;
+
+ /**
+ * Defines the keystore contents for the server, BKS version. Holds just a
+ * single self-generated key. The subject name is "Test Server".
+ */
+ private static final String SERVER_KEYS_BKS =
+ "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41" +
+ "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" +
+ "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" +
+ "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw" +
+ "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" +
+ "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl" +
+ "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy" +
+ "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV" +
+ "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG" +
+ "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU" +
+ "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV" +
+ "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx" +
+ "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR" +
+ "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN" +
+ "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs" +
+ "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck" +
+ "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM" +
+ "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI" +
+ "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f" +
+ "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx" +
+ "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt" +
+ "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw" +
+ "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" +
+ "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
+
+ private String PASSWORD = "android";
+
+ /**
+ * Loads a keystore from a base64-encoded String. Returns the KeyManager[]
+ * for the result.
+ */
+ private KeyManager[] getKeyManagers() throws Exception {
+ byte[] bytes = Base64.decode(SERVER_KEYS_BKS, Base64.DEFAULT);
+ InputStream inputStream = new ByteArrayInputStream(bytes);
+
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keyStore.load(inputStream, PASSWORD.toCharArray());
+ inputStream.close();
+
+ String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
+ keyManagerFactory.init(keyStore, PASSWORD.toCharArray());
+
+ return keyManagerFactory.getKeyManagers();
+ }
+
+
+ public ServerThread(TestWebServer server, boolean ssl) throws Exception {
+ super("ServerThread");
+ mServer = server;
+ mIsSsl = ssl;
+ int retry = 3;
+ while (true) {
+ try {
+ if (mIsSsl) {
+ mSslContext = SSLContext.getInstance("TLS");
+ mSslContext.init(getKeyManagers(), null, null);
+ mSocket = mSslContext.getServerSocketFactory().createServerSocket(
+ SSL_SERVER_PORT);
+ } else {
+ mSocket = new ServerSocket(SERVER_PORT);
+ }
+ return;
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ if (--retry == 0) {
+ throw e;
+ }
+ // sleep in case server socket is still being closed
+ Thread.sleep(1000);
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0);
+ while (!mIsCancelled) {
+ try {
+ Socket socket = mSocket.accept();
+ DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
+ conn.bind(socket, params);
+
+ // Determine whether we need to shutdown early before
+ // parsing the response since conn.close() will crash
+ // for SSL requests due to UnsupportedOperationException.
+ HttpRequest request = conn.receiveRequestHeader();
+ if (isShutdownRequest(request)) {
+ mIsCancelled = true;
+ }
+
+ HttpResponse response = mServer.getResponse(request);
+ conn.sendResponseHeader(response);
+ conn.sendResponseEntity(response);
+ conn.close();
+
+ } catch (IOException e) {
+ // normal during shutdown, ignore
+ Log.w(TAG, e);
+ } catch (HttpException e) {
+ Log.w(TAG, e);
+ } catch (InterruptedException e) {
+ Log.w(TAG, e);
+ } catch (UnsupportedOperationException e) {
+ // DefaultHttpServerConnection's close() throws an
+ // UnsupportedOperationException.
+ Log.w(TAG, e);
+ }
+ }
+ try {
+ mSocket.close();
+ } catch (IOException ignored) {
+ // safe to ignore
+ }
+ }
+
+ private boolean isShutdownRequest(HttpRequest request) {
+ RequestLine requestLine = request.getRequestLine();
+ String uriString = requestLine.getUri();
+ URI uri = URI.create(uriString);
+ String path = uri.getPath();
+ return path.equals(SHUTDOWN_PREFIX);
+ }
+ }
+}
diff --git a/android_webview/lib/aw_browser_dependency_factory_impl.cc b/android_webview/lib/aw_browser_dependency_factory_impl.cc
index f940b104..f31bb64 100644
--- a/android_webview/lib/aw_browser_dependency_factory_impl.cc
+++ b/android_webview/lib/aw_browser_dependency_factory_impl.cc
@@ -5,8 +5,12 @@
#include "android_webview/lib/aw_browser_dependency_factory_impl.h"
// TODO(joth): Componentize or remove chrome/... dependencies.
+#include "android_webview/browser/net/aw_network_delegate.h"
#include "android_webview/native/aw_contents_container.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
@@ -14,6 +18,11 @@
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "content/public/browser/web_contents.h"
#include "ipc/ipc_message.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using content::BrowserContext;
+using content::WebContents;
namespace android_webview {
@@ -49,13 +58,44 @@ void AwBrowserDependencyFactoryImpl::InstallInstance() {
SetInstance(g_lazy_instance.Pointer());
}
-content::WebContents*
-AwBrowserDependencyFactoryImpl::CreateWebContents(bool incognito) {
+// Initializing the Network Delegate here is only a temporary solution until we
+// build an Android WebView specific BrowserContext that can handle building
+// this internally.
+void AwBrowserDependencyFactoryImpl::InitializeNetworkDelegateOnIOThread(
+ net::URLRequestContextGetter* normal_context,
+ net::URLRequestContextGetter* incognito_context) {
+ network_delegate_.reset(new AwNetworkDelegate());
+ normal_context->GetURLRequestContext()->set_network_delegate(
+ network_delegate_.get());
+ incognito_context->GetURLRequestContext()->set_network_delegate(
+ network_delegate_.get());
+}
+
+void AwBrowserDependencyFactoryImpl::EnsureNetworkDelegateInitialized() {
+ if (initialized_network_delegate_)
+ return;
+ initialized_network_delegate_ = true;
Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile();
- if (incognito)
- profile = profile->GetOffTheRecordProfile();
+ profile->GetRequestContext()->GetNetworkTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &AwBrowserDependencyFactoryImpl::InitializeNetworkDelegateOnIOThread,
+ base::Unretained(this),
+ make_scoped_refptr(profile->GetRequestContext()),
+ make_scoped_refptr(
+ profile->GetOffTheRecordProfile()->GetRequestContext())));
+}
+
+content::BrowserContext* AwBrowserDependencyFactoryImpl::GetBrowserContext(
+ bool incognito) {
+ EnsureNetworkDelegateInitialized();
+ Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile();
+ return incognito ? profile->GetOffTheRecordProfile() : profile;
+}
- return content::WebContents::Create(profile, 0, MSG_ROUTING_NONE, 0);
+WebContents* AwBrowserDependencyFactoryImpl::CreateWebContents(bool incognito) {
+ return content::WebContents::Create(
+ GetBrowserContext(incognito), 0, MSG_ROUTING_NONE, 0);
}
AwContentsContainer* AwBrowserDependencyFactoryImpl::CreateContentsContainer(
diff --git a/android_webview/lib/aw_browser_dependency_factory_impl.h b/android_webview/lib/aw_browser_dependency_factory_impl.h
index fae835f..4b9a003 100644
--- a/android_webview/lib/aw_browser_dependency_factory_impl.h
+++ b/android_webview/lib/aw_browser_dependency_factory_impl.h
@@ -8,9 +8,16 @@
#include "android_webview/native/aw_browser_dependency_factory.h"
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
namespace android_webview {
+class AwNetworkDelegate;
+
class AwBrowserDependencyFactoryImpl : public AwBrowserDependencyFactory {
public:
AwBrowserDependencyFactoryImpl();
@@ -20,6 +27,7 @@ class AwBrowserDependencyFactoryImpl : public AwBrowserDependencyFactory {
static void InstallInstance();
// AwBrowserDependencyFactory
+ virtual content::BrowserContext* GetBrowserContext(bool incognito) OVERRIDE;
virtual content::WebContents* CreateWebContents(bool incognito) OVERRIDE;
virtual AwContentsContainer* CreateContentsContainer(
content::WebContents* contents) OVERRIDE;
@@ -27,6 +35,16 @@ class AwBrowserDependencyFactoryImpl : public AwBrowserDependencyFactory {
OVERRIDE;
private:
+ void InitializeNetworkDelegateOnIOThread(
+ net::URLRequestContextGetter* normal_context,
+ net::URLRequestContextGetter* incognito_context);
+ void EnsureNetworkDelegateInitialized();
+
+ // Constructed and assigned on the IO thread.
+ scoped_ptr<AwNetworkDelegate> network_delegate_;
+ // Set on the UI thread.
+ bool initialized_network_delegate_;
+
DISALLOW_COPY_AND_ASSIGN(AwBrowserDependencyFactoryImpl);
};
diff --git a/android_webview/lib/aw_content_browser_client.cc b/android_webview/lib/aw_content_browser_client.cc
index aeaedf2..2a07343 100644
--- a/android_webview/lib/aw_content_browser_client.cc
+++ b/android_webview/lib/aw_content_browser_client.cc
@@ -4,6 +4,7 @@
#include "android_webview/lib/aw_content_browser_client.h"
+#include "android_webview/browser/aw_cookie_access_policy.h"
#include "android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h"
namespace android_webview {
@@ -20,4 +21,38 @@ void AwContentBrowserClient::ResourceDispatcherHostCreated() {
AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated();
}
+bool AwContentBrowserClient::AllowGetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id) {
+ // Not base-calling into ChromeContentBrowserClient as we are not dependent
+ // on chrome/ for any cookie policy decisions.
+ return AwCookieAccessPolicy::GetInstance()->AllowGetCookie(url,
+ first_party,
+ cookie_list,
+ context,
+ render_process_id,
+ render_view_id);
+}
+
+bool AwContentBrowserClient::AllowSetCookie(const GURL& url,
+ const GURL& first_party,
+ const std::string& cookie_line,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id,
+ net::CookieOptions* options) {
+ // Not base-calling into ChromeContentBrowserClient as we are not dependent
+ // on chrome/ for any cookie policy decisions.
+ return AwCookieAccessPolicy::GetInstance()->AllowSetCookie(url,
+ first_party,
+ cookie_line,
+ context,
+ render_process_id,
+ render_view_id,
+ options);
+}
+
} // namespace android_webview
diff --git a/android_webview/lib/aw_content_browser_client.h b/android_webview/lib/aw_content_browser_client.h
index 0972032..8b75c99 100644
--- a/android_webview/lib/aw_content_browser_client.h
+++ b/android_webview/lib/aw_content_browser_client.h
@@ -18,6 +18,19 @@ class AwContentBrowserClient : public chrome::ChromeContentBrowserClient {
// Overriden methods from ContentBrowserClient.
virtual void ResourceDispatcherHostCreated() OVERRIDE;
+ virtual bool AllowGetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id) OVERRIDE;
+ virtual bool AllowSetCookie(const GURL& url,
+ const GURL& first_party,
+ const std::string& cookie_line,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_view_id,
+ net::CookieOptions* options) OVERRIDE;
};
} // namespace android_webview
diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc
index cb63594..cabfbbd 100644
--- a/android_webview/native/android_webview_jni_registrar.cc
+++ b/android_webview/native/android_webview_jni_registrar.cc
@@ -6,6 +6,7 @@
#include "android_webview/native/android_web_view_util.h"
#include "android_webview/native/aw_contents.h"
+#include "android_webview/native/cookie_manager.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "chrome/browser/component/web_contents_delegate_android/component_jni_registrar.h"
@@ -15,6 +16,7 @@ namespace android_webview {
static base::android::RegistrationMethod kWebViewRegisteredMethods[] = {
{ "AndroidWebViewUtil", RegisterAndroidWebViewUtil },
{ "AwContents", RegisterAwContents },
+ { "CookieManager", RegisterCookieManager },
};
bool RegisterJni(JNIEnv* env) {
diff --git a/android_webview/native/aw_browser_dependency_factory.h b/android_webview/native/aw_browser_dependency_factory.h
index ebf03fd..7914069 100644
--- a/android_webview/native/aw_browser_dependency_factory.h
+++ b/android_webview/native/aw_browser_dependency_factory.h
@@ -8,6 +8,7 @@
#include "base/basictypes.h"
namespace content {
+class BrowserContext;
class JavaScriptDialogCreator;
class WebContents;
}
@@ -35,6 +36,9 @@ class AwBrowserDependencyFactory {
// Returns the singleton instance. |SetInstance| must have been called.
static AwBrowserDependencyFactory* GetInstance();
+ // Returns the current browser context based on the specified mode.
+ virtual content::BrowserContext* GetBrowserContext(bool incognito) = 0;
+
// Constructs and returns ownership of a WebContents instance.
virtual content::WebContents* CreateWebContents(bool incognito) = 0;
diff --git a/android_webview/native/cookie_manager.cc b/android_webview/native/cookie_manager.cc
new file mode 100644
index 0000000..fbbfc06
--- /dev/null
+++ b/android_webview/native/cookie_manager.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2011 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.
+
+#include "android_webview/native/cookie_manager.h"
+
+#include "android_webview/browser/aw_cookie_access_policy.h"
+#include "android_webview/native/aw_browser_dependency_factory.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_options.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "jni/CookieManager_jni.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertJavaStringToUTF16;
+using content::BrowserThread;
+using net::CookieList;
+using net::CookieMonster;
+using net::URLRequestContextGetter;
+
+// This class is only available when building the chromium back-end for andriod
+// webview: it is required where API backward compatibility demands that the UI
+// thread must block waiting on other threads e.g. to obtain a synchronous
+// return value. Long term, asynchronous overloads of all such methods will be
+// added in the public API, and and no new uses of this will be allowed.
+class ScopedAllowWaitForLegacyWebViewApi {
+ private:
+ base::ThreadRestrictions::ScopedAllowWait wait;
+};
+
+// CookieManager should be refactored to not require all tasks accessing the
+// CookieStore to be piped through the IO thread. It is currently required as
+// the URLRequestContext provides the easiest mechanism for accessing the
+// CookieStore, but the CookieStore is threadsafe. In the future, we may
+// instead want to inject an explicit CookieStore dependency into this object
+// during process initialization to avoid depending on the URLRequestContext.
+//
+// In addition to the IO thread being the easiest access mechanism, it is also
+// used because the async cookie tasks need to be processed on a different
+// thread than the caller from Java, which can be any thread. But, the calling
+// thread will be 'dead' blocked waiting on the async task to complete, so we
+// need it to complete on a 'live' (non-blocked) thread that is still pumping
+// messages.
+//
+// We could refactor to only provide an asynchronous (but thread safe) API on
+// the native side, and move this support for legacy synchronous blocking
+// messages into a java-side worker thread.
+
+namespace android_webview {
+
+namespace {
+
+typedef base::Callback<void(scoped_refptr<URLRequestContextGetter>,
+ base::WaitableEvent*)> CookieTask;
+
+// Executes the |callback| task on the IO thread and |wait_for_completion|
+// should only be true if the Java API method returns a value or is explicitly
+// stated to be synchronous.
+static void ExecCookieTask(const CookieTask& callback,
+ const bool wait_for_completion) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ content::BrowserContext* context =
+ android_webview::AwBrowserDependencyFactory::GetInstance()->
+ GetBrowserContext(false);
+ if (!context)
+ return;
+
+ scoped_refptr<URLRequestContextGetter> context_getter(
+ context->GetRequestContext());
+
+ if (wait_for_completion) {
+ base::WaitableEvent completion(false, false);
+
+ context->GetRequestContext()->GetNetworkTaskRunner()->PostTask(
+ FROM_HERE, base::Bind(callback, context_getter, &completion));
+
+ ScopedAllowWaitForLegacyWebViewApi wait;
+ completion.Wait();
+ } else {
+ base::WaitableEvent* cb = NULL;
+ context->GetRequestContext()->GetNetworkTaskRunner()->PostTask(
+ FROM_HERE, base::Bind(callback, context_getter, cb));
+ }
+}
+
+} // namespace
+
+static void SetAcceptCookie(JNIEnv* env, jobject obj, jboolean accept) {
+ AwCookieAccessPolicy::GetInstance()->SetGlobalAllowAccess(accept);
+}
+
+static jboolean AcceptCookie(JNIEnv* env, jobject obj) {
+ return AwCookieAccessPolicy::GetInstance()->GetGlobalAllowAccess();
+}
+
+namespace {
+
+// The CookieManager API does not return a value for SetCookie,
+// so we don't need to propagate the |success| value back to the caller.
+static void SetCookieCompleted(bool success) {
+}
+
+static void SetCookieAsyncHelper(
+ const GURL& host,
+ const std::string& value,
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+ DCHECK(!completion);
+ net::CookieOptions options;
+ options.set_include_httponly();
+
+ context_getter->GetURLRequestContext()->cookie_store()->
+ SetCookieWithOptionsAsync(host, value, options,
+ base::Bind(&SetCookieCompleted));
+}
+
+} // namespace
+
+static void SetCookie(JNIEnv* env, jobject obj, jstring url, jstring value) {
+ GURL host(ConvertJavaStringToUTF16(env, url));
+ std::string cookie_value(ConvertJavaStringToUTF8(env, value));
+
+ ExecCookieTask(base::Bind(&SetCookieAsyncHelper, host, cookie_value), false);
+}
+
+namespace {
+
+static void GetCookieValueCompleted(base::WaitableEvent* completion,
+ std::string* result,
+ const std::string& value) {
+ *result = value;
+ DCHECK(completion);
+ completion->Signal();
+}
+
+static void GetCookieValueAsyncHelper(
+ const GURL& host,
+ std::string* result,
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+
+ net::CookieOptions options;
+ options.set_include_httponly();
+
+ context_getter->GetURLRequestContext()->cookie_store()->
+ GetCookiesWithOptionsAsync(host, options,
+ base::Bind(&GetCookieValueCompleted,
+ completion,
+ result));
+}
+
+} // namespace
+
+static jstring GetCookie(JNIEnv* env, jobject obj, jstring url) {
+ GURL host(ConvertJavaStringToUTF16(env, url));
+ std::string cookie_value;
+ ExecCookieTask(base::Bind(&GetCookieValueAsyncHelper, host, &cookie_value),
+ true);
+
+ return base::android::ConvertUTF8ToJavaString(env, cookie_value).Release();
+}
+
+namespace {
+
+static void RemoveSessionCookieCompleted(int num_deleted) {
+ // The CookieManager API does not return a value for removeSessionCookie,
+ // so we don't need to propagate the |num_deleted| value back to the caller.
+}
+
+static void RemoveSessionCookieAsyncHelper(
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+ DCHECK(!completion);
+ net::CookieOptions options;
+ options.set_include_httponly();
+
+ CookieMonster* monster = context_getter->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ monster->DeleteSessionCookiesAsync(base::Bind(&RemoveSessionCookieCompleted));
+}
+
+} // namespace
+
+static void RemoveSessionCookie(JNIEnv* env, jobject obj) {
+ ExecCookieTask(base::Bind(&RemoveSessionCookieAsyncHelper), false);
+}
+
+namespace {
+
+static void RemoveAllCookieCompleted(int num_deleted) {
+ // The CookieManager API does not return a value for removeAllCookie,
+ // so we don't need to propagate the |num_deleted| value back to the caller.
+}
+
+static void RemoveAllCookieAsyncHelper(
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+ DCHECK(!completion);
+ CookieMonster* monster = context_getter->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ monster->DeleteAllAsync(base::Bind(&RemoveAllCookieCompleted));
+}
+
+} // namespace
+
+static void RemoveAllCookie(JNIEnv* env, jobject obj) {
+ ExecCookieTask(base::Bind(&RemoveAllCookieAsyncHelper), false);
+}
+
+static void RemoveExpiredCookie(JNIEnv* env, jobject obj) {
+ // HasCookies will call GetAllCookiesAsync, which in turn will force a GC.
+ HasCookies(env, obj);
+}
+
+namespace {
+
+static void HasCookiesCompleted(base::WaitableEvent* completion,
+ bool* result,
+ const CookieList& cookies) {
+ *result = cookies.size() != 0;
+ DCHECK(completion);
+ completion->Signal();
+}
+
+static void HasCookiesAsyncHelper(
+ bool* result,
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+
+ CookieMonster* monster = context_getter->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ monster->GetAllCookiesAsync(base::Bind(&HasCookiesCompleted, completion,
+ result));
+}
+
+} // namespace
+
+static jboolean HasCookies(JNIEnv* env, jobject obj) {
+ bool has_cookies;
+ ExecCookieTask(base::Bind(&HasCookiesAsyncHelper, &has_cookies), true);
+ return has_cookies;
+}
+
+namespace {
+
+static void AllowFileSchemeCookiesAsyncHelper(
+ bool* accept,
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+
+ CookieMonster* monster = context_getter->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ *accept = monster->IsCookieableScheme("file");
+
+ DCHECK(completion);
+ completion->Signal();
+}
+
+} // namespace
+
+static jboolean AllowFileSchemeCookies(JNIEnv* env, jclass obj) {
+ bool accept;
+ ExecCookieTask(base::Bind(&AllowFileSchemeCookiesAsyncHelper, &accept), true);
+ return accept;
+}
+
+namespace {
+
+static void SetAcceptFileSchemeCookiesAsyncHelper(
+ bool accept,
+ scoped_refptr<URLRequestContextGetter> context_getter,
+ base::WaitableEvent* completion) {
+
+ CookieMonster* monster = context_getter->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ monster->SetEnableFileScheme(accept);
+
+ DCHECK(completion);
+ completion->Signal();
+}
+
+} // namespace
+
+static void SetAcceptFileSchemeCookies(JNIEnv* env, jclass obj,
+ jboolean accept) {
+ ExecCookieTask(base::Bind(&SetAcceptFileSchemeCookiesAsyncHelper, accept),
+ true);
+}
+
+bool RegisterCookieManager(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // android_webview namespace
diff --git a/android_webview/native/cookie_manager.h b/android_webview/native/cookie_manager.h
new file mode 100644
index 0000000..2bb2ca6
--- /dev/null
+++ b/android_webview/native/cookie_manager.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2011 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.
+
+#ifndef ANDROID_WEBVIEW_NATIVE_COOKIE_MANAGER_H_
+#define ANDROID_WEBVIEW_NATIVE_COOKIE_MANAGER_H_
+
+#include <jni.h>
+
+namespace android_webview {
+
+bool RegisterCookieManager(JNIEnv* env);
+
+} // namespace android_webview;
+
+#endif // ANDROID_WEBVIEW_NATIVE_COOKIE_MANAGER_H_
diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp
index 0cbb379..3e20fc9 100644
--- a/android_webview/native/webview_native.gyp
+++ b/android_webview/native/webview_native.gyp
@@ -31,6 +31,8 @@
'aw_contents.h',
'aw_web_contents_delegate.cc',
'aw_web_contents_delegate.h',
+ 'cookie_manager.cc',
+ 'cookie_manager.h',
'intercepted_request_data.cc',
'intercepted_request_data.h',
],
@@ -41,6 +43,7 @@
'sources': [
'../java/src/org/chromium/android_webview/AndroidWebViewUtil.java',
'../java/src/org/chromium/android_webview/AwContents.java',
+ '../java/src/org/chromium/android_webview/CookieManager.java',
'../java/src/org/chromium/android_webview/InterceptedRequestData.java',
],
'variables': {