Add Third Party Cookie API
Bug: 11678084
Change-Id: Ib92dedda8fbb822c64c2003426f7927a4409f401
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index 6416b73..b34cfc8 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -111,7 +111,9 @@
private static final String QUERY_REDIRECT_PATH = "/alt_redirect";
private static final String DELAY_PREFIX = "/delayed";
private static final String BINARY_PREFIX = "/binary";
+ private static final String SET_COOKIE_PREFIX = "/setcookie";
private static final String COOKIE_PREFIX = "/cookie";
+ private static final String LINKED_SCRIPT_PREFIX = "/linkedscriptprefix";
private static final String AUTH_PREFIX = "/auth";
private static final String SHUTDOWN_PREFIX = "/shutdown";
public static final String NOLENGTH_POSTFIX = "nolength";
@@ -354,7 +356,6 @@
return sb.toString();
}
-
/**
* Return an absolute URL that indirectly refers to the given asset.
* When a client fetches this URL, the server will respond with a temporary redirect (302)
@@ -400,6 +401,43 @@
return sb.toString();
}
+ /**
+ * getSetCookieUrl returns a URL that attempts to set the cookie
+ * "key=value" when fetched.
+ * @param path a suffix to disambiguate mulitple Cookie URLs.
+ * @param key the key of the cookie.
+ * @return the url for a page that attempts to set the cookie.
+ */
+ public String getSetCookieUrl(String path, String key, String value) {
+ StringBuilder sb = new StringBuilder(getBaseUri());
+ sb.append(SET_COOKIE_PREFIX);
+ sb.append(path);
+ sb.append("?key=");
+ sb.append(key);
+ sb.append("&value=");
+ sb.append(value);
+ return sb.toString();
+ }
+
+ /**
+ * getLinkedScriptUrl returns a URL for a page with a script tag where
+ * src equals the URL passed in.
+ * @param path a suffix to disambiguate mulitple Linked Script URLs.
+ * @param url the src of the script tag.
+ * @return the url for the page with the script link in.
+ */
+ public String getLinkedScriptUrl(String path, String url) {
+ StringBuilder sb = new StringBuilder(getBaseUri());
+ sb.append(LINKED_SCRIPT_PREFIX);
+ sb.append(path);
+ sb.append("?url=");
+ try {
+ sb.append(URLEncoder.encode(url, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ }
+ return sb.toString();
+ }
+
public String getBinaryUrl(String mimeType, int contentLength) {
StringBuilder sb = new StringBuilder(getBaseUri());
sb.append(BINARY_PREFIX);
@@ -681,8 +719,20 @@
}
response.addHeader("Set-Cookie", "count=" + count + "; path=" + COOKIE_PREFIX);
- response.setEntity(createEntity("<html><head><title>" + cookieString +
- "</title></head><body>" + cookieString + "</body></html>"));
+ response.setEntity(createPage(cookieString.toString(), cookieString.toString()));
+ } else if (path.startsWith(SET_COOKIE_PREFIX)) {
+ response = createResponse(HttpStatus.SC_OK);
+ Uri parsedUri = Uri.parse(uriString);
+ String key = parsedUri.getQueryParameter("key");
+ String value = parsedUri.getQueryParameter("value");
+ String cookie = key + "=" + value;
+ response.addHeader("Set-Cookie", cookie);
+ response.setEntity(createPage(cookie, cookie));
+ } else if (path.startsWith(LINKED_SCRIPT_PREFIX)) {
+ response = createResponse(HttpStatus.SC_OK);
+ String src = Uri.parse(uriString).getQueryParameter("url");
+ String scriptTag = "<script src=\"" + src + "\"></script>";
+ response.setEntity(createPage("LinkedScript", scriptTag));
} else if (path.equals(USERAGENT_PATH)) {
response = createResponse(HttpStatus.SC_OK);
Header agentHeader = request.getFirstHeader("User-Agent");
@@ -690,8 +740,7 @@
if (agentHeader != null) {
agent = agentHeader.getValue();
}
- response.setEntity(createEntity("<html><head><title>" + agent + "</title></head>" +
- "<body>" + agent + "</body></html>"));
+ response.setEntity(createPage(agent, agent));
} else if (path.equals(TEST_DOWNLOAD_PATH)) {
response = createTestDownloadResponse(Uri.parse(uriString));
} else if (path.equals(SHUTDOWN_PREFIX)) {
@@ -772,12 +821,7 @@
// Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is Locale-dependent.
String reason = getReasonString(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()));
+ response.setEntity(createPage(reason, reason));
}
return response;
}
@@ -796,6 +840,14 @@
return null;
}
+ /**
+ * Create a string entity for a bare bones html page with provided title and body.
+ */
+ private static StringEntity createPage(String title, String bodyContent) {
+ return createEntity("<html><head><title>" + title + "</title></head>" +
+ "<body>" + bodyContent + "</body></html>");
+ }
+
private static HttpResponse createTestDownloadResponse(Uri uri) throws IOException {
String downloadId = uri.getQueryParameter(DOWNLOAD_ID_PARAMETER);
int numBytes = uri.getQueryParameter(NUM_BYTES_PARAMETER) != null
diff --git a/tests/src/android/webkit/cts/WebViewOnUiThread.java b/tests/src/android/webkit/cts/WebViewOnUiThread.java
index c1032a6..4a4d62f 100644
--- a/tests/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/tests/src/android/webkit/cts/WebViewOnUiThread.java
@@ -31,6 +31,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.webkit.DownloadListener;
+import android.webkit.CookieManager;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
@@ -737,6 +738,24 @@
});
}
+ public void setAcceptThirdPartyCookies(final boolean accept) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, accept);
+ }
+ });
+ }
+
+ public boolean acceptThirdPartyCookies() {
+ return getValue(new ValueGetter<Boolean>() {
+ @Override
+ public Boolean capture() {
+ return CookieManager.getInstance().acceptThirdPartyCookies(mWebView);
+ }
+ });
+ }
+
/**
* Helper for running code on the UI thread where an exception is
* a test failure. If this is already the UI thread then it runs
@@ -766,7 +785,7 @@
return mWebView;
}
- private <T> T getValue(ValueGetter<T> getter) {
+ private<T> T getValue(ValueGetter<T> getter) {
runOnUiThread(getter);
return getter.getValue();
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
index cb3ec73..f368dc0 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
@@ -36,8 +36,9 @@
private static final int TEST_TIMEOUT = 5000;
- private WebViewOnUiThread mOnUiThread;
+ private WebView mWebView;
private CookieManager mCookieManager;
+ private WebViewOnUiThread mOnUiThread;
public CookieManagerTest() {
super("com.android.cts.stub", CookieSyncManagerStubActivity.class);
@@ -46,9 +47,9 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- WebView webview = getActivity().getWebView();
- if (webview != null) {
- mOnUiThread = new WebViewOnUiThread(this, webview);
+ mWebView = getActivity().getWebView();
+ if (mWebView != null) {
+ mOnUiThread = new WebViewOnUiThread(this, mWebView);
mCookieManager = CookieManager.getInstance();
assertNotNull(mCookieManager);
@@ -310,6 +311,63 @@
assertFalse(anyDeleted.get());
}
+ public void testThirdPartyCookie() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ CtsTestServer server = null;
+ try {
+ // In theory we need two servers to test this, one server ('the first party')
+ // which returns a response with a link to a second server ('the third party')
+ // at different origin. This second server attempts to set a cookie which should
+ // fail if AcceptThirdPartyCookie() is false.
+ // Strictly according to the letter of RFC6454 it should be possible to set this
+ // situation up with two TestServers on different ports (these count as having
+ // different origins) but Chrome is not strict about this and does not check the
+ // port. Instead we cheat making some of the urls come from localhost and some
+ // from 127.0.0.1 which count (both in theory and pratice) as having different
+ // origins.
+ server = new CtsTestServer(getActivity());
+
+ // Turn on Javascript (otherwise <script> aren't fetched spoiling the test).
+ mOnUiThread.getSettings().setJavaScriptEnabled(true);
+
+ // Turn global allow on.
+ mCookieManager.setAcceptCookie(true);
+ assertTrue(mCookieManager.acceptCookie());
+
+ // When third party cookies are disabled...
+ mOnUiThread.setAcceptThirdPartyCookies(false);
+ assertFalse(mOnUiThread.acceptThirdPartyCookies());
+
+ // ...we can't set third party cookies.
+ // First on the third party server we get a url which tries to set a cookie.
+ String cookieUrl = toThirdPartyUrl(
+ server.getSetCookieUrl("cookie_1.js", "test1", "value1"));
+ // Then we create a url on the first party server which links to the first url.
+ String url = server.getLinkedScriptUrl("/content_1.html", cookieUrl);
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ assertNull(mCookieManager.getCookie(cookieUrl));
+
+ // When third party cookies are enabled...
+ mOnUiThread.setAcceptThirdPartyCookies(true);
+ assertTrue(mOnUiThread.acceptThirdPartyCookies());
+
+ // ...we can set third party cookies.
+ cookieUrl = toThirdPartyUrl(
+ server.getSetCookieUrl("/cookie_2.js", "test2", "value2"));
+ url = server.getLinkedScriptUrl("/content_2.html", cookieUrl);
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ waitForCookie(cookieUrl);
+ String cookie = mCookieManager.getCookie(cookieUrl);
+ assertNotNull(cookie);
+ assertTrue(cookie.contains("test2"));
+ } finally {
+ if (server != null) server.shutdown();
+ mOnUiThread.getSettings().setJavaScriptEnabled(false);
+ }
+ }
+
public void testb3167208() throws Exception {
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
@@ -379,4 +437,13 @@
fail("Unexpected error while running on UI thread: " + t.getMessage());
}
}
-}
+
+ /**
+ * Makes a url look as if it comes from a different host.
+ * @param url the url to fake.
+ * @return the resulting url after faking.
+ */
+ public String toThirdPartyUrl(String url) {
+ return url.replace("localhost", "127.0.0.1");
+ }
+ }