Commit 1989515c authored by Benoit Lize's avatar Benoit Lize Committed by Commit Bot

customtabs: Add CustomTabs APIs for detached parallel requests.

This CL adds the CustomTabs "client" APIs for parallel request:
- Enable and query parallel request state
- Get the URL and requested origin from the Intent

Bug: 816837
Change-Id: I6c5cc86453a941f08f118be6229bf3a2c497d344
Reviewed-on: https://chromium-review.googlesource.com/962761Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Commit-Queue: Benoit L <lizeb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543122}
parent 88b034e5
......@@ -147,6 +147,7 @@ class ClientManager {
private String mPredictedUrl;
private long mLastMayLaunchUrlTimestamp;
private int mSpeculationMode;
private boolean mAllowParallelRequest;
public SessionParams(Context context, int uid, DisconnectCallback callback,
PostMessageHandler postMessageHandler) {
......@@ -616,6 +617,17 @@ class ClientManager {
: params.mSpeculationMode;
}
public synchronized void setAllowParallelRequestForSession(
CustomTabsSessionToken session, boolean allowed) {
SessionParams params = mSessionParams.get(session);
if (params != null) params.mAllowParallelRequest = allowed;
}
public synchronized boolean getAllowParallelRequestForSession(CustomTabsSessionToken session) {
SessionParams params = mSessionParams.get(session);
return params != null ? params.mAllowParallelRequest : false;
}
/**
* Returns whether an origin is first-party with respect to a session, that is if the
* application linked to the session has a relation with the provided origin. This does not
......
......@@ -147,6 +147,12 @@ public class CustomTabsConnection {
// TODO(lizeb): Move to the support library.
@VisibleForTesting
static final String REDIRECT_ENDPOINT_KEY = "android.support.customtabs.REDIRECT_ENDPOINT";
@VisibleForTesting
static final String PARALLEL_REQUEST_REFERRER_KEY =
"android.support.customtabs.PARALLEL_REQUEST_REFERRER";
@VisibleForTesting
static final String PARALLEL_REQUEST_URL_KEY =
"android.support.customtabs.PARALLEL_REQUEST_URL";
private static final CustomTabsConnection sInstance =
AppHooks.get().createCustomTabsConnection();
......@@ -894,6 +900,12 @@ public class CustomTabsConnection {
// processing from now on.
if (mWarmupTasks != null) mWarmupTasks.cancel();
maybePreconnectToRedirectEndpoint(session, url, intent);
maybeStartParallelRequest(session, intent);
}
private void maybePreconnectToRedirectEndpoint(
CustomTabsSessionToken session, String url, Intent intent) {
// For the preconnection to not be a no-op, we need more than just the native library.
if (!ChromeBrowserInitializer.getInstance(mContext).hasNativeInitializationCompleted()) {
return;
......@@ -914,19 +926,29 @@ public class CustomTabsConnection {
Profile.getLastUsedProfile(), redirectEndpoint.toString());
}
/** @return Whether {@code session} can create a parallel request for a given {@code origin}. */
private void maybeStartParallelRequest(CustomTabsSessionToken session, Intent intent) {
if (!mClientManager.getAllowParallelRequestForSession(session)) return;
Uri referrer = intent.getParcelableExtra(PARALLEL_REQUEST_REFERRER_KEY);
Uri url = intent.getParcelableExtra(PARALLEL_REQUEST_URL_KEY);
if (referrer == null || url == null) return;
startParallelRequest(session, url, referrer);
}
/** @return Whether {@code session} can create a parallel request for a given
* {@code referrer}.
*/
@VisibleForTesting
boolean canDoParallelRequest(CustomTabsSessionToken session, String origin) {
boolean canDoParallelRequest(CustomTabsSessionToken session, Uri referrer) {
ThreadUtils.assertOnUiThread();
// The restrictions are:
// - Native initialization: Required to get the profile, and the feature state.
// - Feature check
// - The origin is allowed.
// - The referrer's origin is allowed.
//
// TODO(lizeb): Relax the restrictions.
return ChromeBrowserInitializer.getInstance(mContext).hasNativeInitializationCompleted()
&& ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_PARALLEL_REQUEST)
&& mClientManager.isFirstPartyOriginForSession(session, Uri.parse(origin));
&& mClientManager.isFirstPartyOriginForSession(session, referrer);
}
/**
......@@ -934,20 +956,21 @@ public class CustomTabsConnection {
*
* @param session Calling context session.
* @param url URL to send the request to.
* @param origin Origin to use.
* @param referrer Referrer (and first party for cookies) to use.
* @return Whether the request started. False if the session is not authorized to use the
* provided origin, if Chrome hasn't been initialized, or the feature is disabled.
* Also fails if the URL is neither HTTPS not HTTP.
*/
@VisibleForTesting
boolean startParallelRequest(CustomTabsSessionToken session, String url, String origin) {
boolean startParallelRequest(CustomTabsSessionToken session, Uri url, Uri referrer) {
ThreadUtils.assertOnUiThread();
if (TextUtils.isEmpty(url) || !isValid(Uri.parse(url))
|| !canDoParallelRequest(session, origin)) {
if (url.toString().equals("") || !isValid(url)
|| !canDoParallelRequest(session, referrer)) {
return false;
}
nativeCreateAndStartDetachedResourceRequest(Profile.getLastUsedProfile(), url, origin);
nativeCreateAndStartDetachedResourceRequest(
Profile.getLastUsedProfile(), url.toString(), referrer.toString());
return true;
}
......
......@@ -58,6 +58,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.chromium.base.ActivityState;
......@@ -109,6 +110,8 @@ import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
import org.chromium.chrome.test.util.browser.LocationSettingsTestUtil;
import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
import org.chromium.content.browser.test.util.Criteria;
......@@ -198,6 +201,8 @@ public class CustomTabActivityTest {
@Rule
public final ScreenShooter mScreenShooter = new ScreenShooter();
@Rule
public TestRule mProcessor = new Features.InstrumentationProcessor();
@Before
public void setUp() throws Exception {
......@@ -2046,6 +2051,39 @@ public class CustomTabActivityTest {
startHiddenTabAndChangeFragment(false, false);
}
@Test
@SmallTest
@EnableFeatures(ChromeFeatureList.CCT_PARALLEL_REQUEST)
public void testParallelRequest() throws Exception {
String url = mTestServer.getURL("/echoheader?Cookie");
Uri requestUri = Uri.parse(mTestServer.getURL("/set-cookie?acookie"));
final Context context = InstrumentationRegistry.getTargetContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, url);
final CustomTabsSessionToken token =
CustomTabsSessionToken.getSessionTokenFromIntent(intent);
// warmup(), create session, allow parallel requests, allow origin.
CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
final Origin origin = new Origin(requestUri);
Assert.assertTrue(connection.newSession(token));
connection.mClientManager.setAllowParallelRequestForSession(token, true);
ThreadUtils.runOnUiThreadBlocking(() -> {
OriginVerifier.addVerifiedOriginForPackage(
context.getPackageName(), origin, CustomTabsService.RELATION_USE_AS_ORIGIN);
});
intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_URL_KEY, requestUri);
intent.putExtra(
CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, Uri.parse(origin.toString()));
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
String content = JavaScriptUtils.executeJavaScriptAndWaitForResult(
tab.getWebContents(), "document.body.textContent");
Assert.assertEquals("\"acookie\"", content);
}
/**
* Tests the following scenario:
* - warmup() + mayLaunchUrl("http://example.com/page.html#first-fragment")
......
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.customtabs;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.customtabs.CustomTabsService;
import android.support.customtabs.CustomTabsSessionToken;
import android.support.test.InstrumentationRegistry;
......@@ -34,7 +35,6 @@ import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
import org.chromium.content.browser.test.util.JavaScriptUtils;
import org.chromium.net.GURLUtils;
import org.chromium.net.test.EmbeddedTestServer;
/** Tests for detached resource requests. */
......@@ -50,7 +50,7 @@ public class DetachedResourceRequestTest {
private EmbeddedTestServer mServer;
private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
private static final String ORIGIN = "http://cats.google.com";
private static final Uri ORIGIN = Uri.parse("http://cats.google.com");
@Before
public void setUp() throws Exception {
......@@ -83,8 +83,8 @@ public class DetachedResourceRequestTest {
() -> { Assert.assertFalse(mConnection.canDoParallelRequest(session, ORIGIN)); });
ThreadUtils.runOnUiThreadBlocking(() -> {
String packageName = mContext.getPackageName();
OriginVerifier.addVerifiedOriginForPackage(
packageName, new Origin(ORIGIN), CustomTabsService.RELATION_USE_AS_ORIGIN);
OriginVerifier.addVerifiedOriginForPackage(packageName, new Origin(ORIGIN.toString()),
CustomTabsService.RELATION_USE_AS_ORIGIN);
Assert.assertTrue(mConnection.canDoParallelRequest(session, ORIGIN));
});
}
......@@ -97,12 +97,14 @@ public class DetachedResourceRequestTest {
ThreadUtils.runOnUiThreadBlocking(() -> {
Assert.assertFalse("Should not allow android-app:// scheme",
mConnection.startParallelRequest(
session, "android-app://this.is.an.android.app", ORIGIN));
session, Uri.parse("android-app://this.is.an.android.app"), ORIGIN));
Assert.assertFalse("Should not allow an empty URL",
mConnection.startParallelRequest(session, "", ORIGIN));
mConnection.startParallelRequest(session, Uri.parse(""), ORIGIN));
Assert.assertFalse("Should not allow an arbitrary origin",
mConnection.startParallelRequest(session, "HTTPS://foo.bar", "wrong://origin"));
Assert.assertTrue(mConnection.startParallelRequest(session, "HTTP://foo.bar", ORIGIN));
mConnection.startParallelRequest(
session, Uri.parse("HTTPS://foo.bar"), Uri.parse("wrong://origin")));
Assert.assertTrue(
mConnection.startParallelRequest(session, Uri.parse("HTTP://foo.bar"), ORIGIN));
});
}
......@@ -122,7 +124,7 @@ public class DetachedResourceRequestTest {
});
mServer.start();
String url = mServer.getURL("/echotitle");
Uri url = Uri.parse(mServer.getURL("/echotitle"));
ThreadUtils.runOnUiThread(
() -> Assert.assertTrue(mConnection.startParallelRequest(session, url, ORIGIN)));
cb.waitForCallback(0, 1);
......@@ -134,7 +136,7 @@ public class DetachedResourceRequestTest {
public void testCanSetCookie() throws Exception {
CustomTabsSessionToken session = prepareSession();
mServer = EmbeddedTestServer.createAndStartServer(mContext);
final String url = mServer.getURL("/set-cookie?acookie");
final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie"));
ThreadUtils.runOnUiThreadBlocking(
() -> Assert.assertTrue(mConnection.startParallelRequest(session, url, ORIGIN)));
......@@ -159,7 +161,7 @@ public class DetachedResourceRequestTest {
Assert.assertFalse(prefs.isBlockThirdPartyCookiesEnabled());
prefs.setBlockThirdPartyCookiesEnabled(true);
});
final String url = mServer.getURL("/set-cookie?acookie");
final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie"));
ThreadUtils.runOnUiThreadBlocking(
() -> Assert.assertTrue(mConnection.startParallelRequest(session, url, ORIGIN)));
......@@ -184,9 +186,9 @@ public class DetachedResourceRequestTest {
Assert.assertFalse(prefs.isBlockThirdPartyCookiesEnabled());
prefs.setBlockThirdPartyCookiesEnabled(true);
});
final String url = mServer.getURL("/set-cookie?acookie");
String origin = GURLUtils.getOrigin(url);
CustomTabsSessionToken session = prepareSession(origin);
final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie"));
final Uri origin = Uri.parse(new Origin(url).toString());
CustomTabsSessionToken session = prepareSession(url);
ThreadUtils.runOnUiThreadBlocking(
() -> Assert.assertTrue(mConnection.startParallelRequest(session, url, origin)));
......@@ -205,14 +207,14 @@ public class DetachedResourceRequestTest {
return prepareSession(ORIGIN);
}
private CustomTabsSessionToken prepareSession(String origin) throws Exception {
private CustomTabsSessionToken prepareSession(Uri origin) throws Exception {
final CustomTabsSessionToken session =
CustomTabsSessionToken.createMockSessionTokenForTesting();
Assert.assertTrue(mConnection.newSession(session));
CustomTabsTestUtils.warmUpAndWait();
ThreadUtils.runOnUiThreadBlocking(() -> {
OriginVerifier.addVerifiedOriginForPackage(mContext.getPackageName(),
new Origin(origin), CustomTabsService.RELATION_USE_AS_ORIGIN);
new Origin(origin.toString()), CustomTabsService.RELATION_USE_AS_ORIGIN);
Assert.assertTrue(mConnection.canDoParallelRequest(session, origin));
});
return session;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment