[go: nahoru, domu]

blob: ead0f99c140768843e1e304d4a148fce60b85529 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS;
import android.util.Pair;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwBrowserContextStore;
import org.chromium.android_webview.AwBrowserProcess;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwCookieManager;
import org.chromium.android_webview.WebMessageListener;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.DoNotBatch;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.test.util.RenderProcessHostUtils;
import org.chromium.net.test.util.TestWebServer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Tests the management of multiple AwBrowserContexts (profiles) */
@RunWith(Parameterized.class)
@UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
@DoNotBatch(reason = "Tests focus on manipulation of global profile state")
public class MultiProfileTest extends AwParameterizedTest {
@Rule public MultiProfileTestRule mRule;
private TestAwContentsClient mContentsClient;
public MultiProfileTest(AwSettingsMutation param) {
this.mRule = new MultiProfileTestRule(param.getMutation());
this.mContentsClient = mRule.getContentsClient();
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCreateProfiles() {
final AwBrowserContext nonDefaultProfile1 = mRule.getProfileSync("1", true);
final AwBrowserContext alsoNonDefaultProfile1 = mRule.getProfileSync("1", true);
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext defaultProfileByName = mRule.getProfileSync("Default", true);
final AwBrowserContext nonDefaultProfile2 = mRule.getProfileSync("2", true);
assertNotNull(nonDefaultProfile1);
assertNotNull(nonDefaultProfile2);
assertNotNull(defaultProfile);
Assert.assertSame(nonDefaultProfile1, alsoNonDefaultProfile1);
Assert.assertSame(defaultProfile, defaultProfileByName);
Assert.assertNotSame(nonDefaultProfile1, nonDefaultProfile2);
Assert.assertNotSame(defaultProfile, nonDefaultProfile1);
Assert.assertNotSame(nonDefaultProfile2, defaultProfile);
final List<String> names =
ThreadUtils.runOnUiThreadBlockingNoException(
AwBrowserContextStore::listAllContexts);
Assert.assertTrue(names.contains("1"));
Assert.assertTrue(names.contains("2"));
Assert.assertTrue(names.contains("Default"));
Assert.assertFalse(names.contains("3"));
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testGetProfiles() {
mRule.getProfileSync("Exists", true);
final AwBrowserContext existsProfile1 = mRule.getProfileSync("Exists", false);
final AwBrowserContext existsProfile2 = mRule.getProfileSync("Exists", false);
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext defaultProfileByName = mRule.getProfileSync("Default", false);
final AwBrowserContext notExistsProfile = mRule.getProfileSync("NotExists", false);
assertNotNull(existsProfile1);
assertNotNull(defaultProfile);
Assert.assertNull(notExistsProfile);
Assert.assertSame(existsProfile1, existsProfile2);
Assert.assertSame(defaultProfile, defaultProfileByName);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCannotDeleteDefault() {
mRule.runOnUiThread(
() -> {
Assert.assertThrows(
IllegalArgumentException.class,
() -> {
AwBrowserContextStore.deleteNamedContext("Default");
});
});
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCannotDeleteProfileInUse() {
mRule.getProfileSync("myProfile", true);
mRule.runOnUiThread(
() -> {
Assert.assertThrows(
IllegalStateException.class,
() -> {
AwBrowserContextStore.deleteNamedContext("myProfile");
});
});
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCanDeleteNonExistent() {
mRule.runOnUiThread(
() -> {
Assert.assertFalse(AwBrowserContextStore.deleteNamedContext("DoesNotExist"));
});
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testGetName() {
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext profile1 = mRule.getProfileSync("AwesomeProfile", true);
assertEquals("Default", defaultProfile.getName());
assertEquals("AwesomeProfile", profile1.getName());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testGetRelativePath() {
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext myCoolProfile = mRule.getProfileSync("MyCoolProfile", true);
final AwBrowserContext myOtherCoolProfile =
mRule.getProfileSync("MyOtherCoolProfile", true);
assertEquals("Default", defaultProfile.getRelativePathForTesting());
assertEquals("Profile 1", myCoolProfile.getRelativePathForTesting());
assertEquals("Profile 2", myOtherCoolProfile.getRelativePathForTesting());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSharedPrefsNamesAreCorrectAndUnique() {
final String dataDirSuffix = "MyDataDirSuffix";
AwBrowserProcess.setProcessDataDirSuffixForTesting(dataDirSuffix);
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext myCoolProfile = mRule.getProfileSync("MyCoolProfile", true);
final AwBrowserContext myOtherCoolProfile =
mRule.getProfileSync("MyOtherCoolProfile", true);
final AwBrowserContext myCoolProfileCopy = mRule.getProfileSync("MyCoolProfile", true);
assertEquals(
"WebViewProfilePrefsDefault_MyDataDirSuffix",
defaultProfile.getSharedPrefsNameForTesting());
assertEquals(
"WebViewProfilePrefsProfile 1_MyDataDirSuffix",
myCoolProfile.getSharedPrefsNameForTesting());
assertEquals(
"WebViewProfilePrefsProfile 2_MyDataDirSuffix",
myOtherCoolProfile.getSharedPrefsNameForTesting());
assertEquals(
myCoolProfile.getSharedPrefsNameForTesting(),
myCoolProfileCopy.getSharedPrefsNameForTesting());
AwBrowserProcess.setProcessDataDirSuffixForTesting(null);
assertEquals("WebViewProfilePrefsDefault", defaultProfile.getSharedPrefsNameForTesting());
assertEquals("WebViewProfilePrefsProfile 1", myCoolProfile.getSharedPrefsNameForTesting());
assertEquals(
"WebViewProfilePrefsProfile 2", myOtherCoolProfile.getSharedPrefsNameForTesting());
assertEquals(
myCoolProfile.getSharedPrefsNameForTesting(),
myCoolProfileCopy.getSharedPrefsNameForTesting());
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextOnDestroyedWebViewThrowsException() {
mRule.startBrowserProcess();
final AwBrowserContext otherProfile = mRule.getProfileSync("other-profile", true);
mRule.runOnUiThread(
() -> {
AwContents awContents =
mRule.createAwTestContainerView(mContentsClient).getAwContents();
awContents.destroy();
Assert.assertThrows(
"Cannot set new profile on a WebView that has been destroyed",
IllegalStateException.class,
() -> {
awContents.setBrowserContext(otherProfile);
});
});
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextAfterGetBrowserContextThrowsException() {
mRule.startBrowserProcess();
final AwBrowserContext otherProfile = mRule.getProfileSync("other-profile", true);
mRule.runOnUiThread(
() -> {
AwContents awContents =
mRule.createAwTestContainerView(mContentsClient).getAwContents();
awContents.getBrowserContext();
Assert.assertThrows(
"Cannot set new profile after the current one has been retrieved via. "
+ "WebViewCompat#getProfile",
IllegalStateException.class,
() -> {
awContents.setBrowserContext(otherProfile);
});
});
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextAfterPreviouslySetThrowsException() {
mRule.startBrowserProcess();
final AwBrowserContext myCoolProfile = mRule.getProfileSync("my-profile", true);
final AwBrowserContext myOtherCoolProfile = mRule.getProfileSync("my-other-profile", true);
mRule.runOnUiThread(
() -> {
AwContents awContents =
mRule.createAwTestContainerView(mContentsClient).getAwContents();
awContents.setBrowserContext(myCoolProfile);
Assert.assertThrows(
"Cannot set new profile after one has already been set"
+ "via. WebViewCompat#setProfile",
IllegalStateException.class,
() -> {
awContents.setBrowserContext(myOtherCoolProfile);
});
});
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextAfterEvaluateJavascriptThrowsException() {
mRule.startBrowserProcess();
AwContents awContents = mRule.createAwContents();
ThreadUtils.runOnUiThreadBlockingNoException(
() -> {
awContents.evaluateJavaScript("", null);
return null;
});
final AwBrowserContext myCoolProfile = mRule.getProfileSync("my-profile", true);
mRule.runOnUiThread(
() ->
Assert.assertThrows(
"Cannot set new profile after call to WebView#evaluateJavascript",
IllegalStateException.class,
() -> awContents.setBrowserContext(myCoolProfile)));
}
@Test
@MediumTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextAfterWebViewNavigatedThrowsException() throws Throwable {
mRule.startBrowserProcess();
final AwBrowserContext myCoolProfile = mRule.getProfileSync("my-profile", true);
AwContents awContents = mRule.createAwContents();
TestWebServer webServer = TestWebServer.start();
String url = webServer.setResponse("/URL.html", "", null);
mRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), url);
mRule.runOnUiThread(
() -> {
Assert.assertThrows(
"Cannot set new profile on a WebView that has been previously"
+ " navigated.",
IllegalStateException.class,
() -> {
awContents.setBrowserContext(myCoolProfile);
});
webServer.shutdown();
});
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSetBrowserContextSetsTheCorrectProfileOnAwContents() {
mRule.startBrowserProcess();
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext otherProfile = mRule.getProfileSync("my-profile", true);
final AwContents firstAwContents = mRule.createAwContents();
final AwContents secondAwContents = mRule.createAwContents(otherProfile);
Assert.assertSame(defaultProfile, firstAwContents.getBrowserContext());
Assert.assertSame(otherProfile, secondAwContents.getBrowserContext());
}
@Test
@SmallTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testGetBrowserContextThrowsExceptionIfWebViewDestroyed() {
mRule.startBrowserProcess();
final AwBrowserContext myProfile = mRule.getProfileSync("my-profile", true);
final AwContents awContents = mRule.createAwContents(myProfile);
awContents.destroy();
Assert.assertThrows(
"Cannot get profile for destroyed WebView.",
IllegalStateException.class,
awContents::getBrowserContext);
}
@Test
@LargeTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testWebViewsRunningDifferentProfilesUseCorrectCookieManagers() throws Throwable {
mRule.startBrowserProcess();
final AwBrowserContext defaultProfile = AwBrowserContext.getDefault();
final AwBrowserContext otherProfile = mRule.getProfileSync("my-profile", true);
final AwContents firstAwContents = mRule.createAwContents();
final AwContents secondAwContents = mRule.createAwContents(otherProfile);
AwCookieManager defaultCookieManager = defaultProfile.getCookieManager();
Assert.assertSame(
defaultCookieManager, firstAwContents.getBrowserContext().getCookieManager());
defaultCookieManager.setAcceptCookie(true);
AwCookieManager otherCookieManager = otherProfile.getCookieManager();
Assert.assertSame(
otherCookieManager, secondAwContents.getBrowserContext().getCookieManager());
otherCookieManager.setAcceptCookie(true);
Assert.assertFalse(defaultCookieManager.hasCookies());
Assert.assertFalse(otherCookieManager.hasCookies());
TestWebServer webServer = TestWebServer.start();
String[] cookies = {
"httponly=foo1; HttpOnly",
"strictsamesite=foo2; SameSite=Strict",
"laxsamesite=foo3; SameSite=Lax"
};
List<Pair<String, String>> responseHeaders = new ArrayList<>();
for (String cookie : cookies) {
responseHeaders.add(Pair.create("Set-Cookie", cookie));
}
String path = "/cookie_test.html";
String responseStr = "<html><head><title>TEST!</title></head><body>HELLO!</body></html>";
String url = webServer.setResponse(path, responseStr, responseHeaders);
mRule.loadUrlSync(secondAwContents, mContentsClient.getOnPageFinishedHelper(), url);
AwActivityTestRule.pollInstrumentationThread(
() -> otherCookieManager.getCookie(url) != null);
Assert.assertTrue(otherCookieManager.hasCookies());
assertNotNull(otherCookieManager.getCookie(url));
validateCookies(otherCookieManager, url, "httponly", "strictsamesite", "laxsamesite");
otherCookieManager.removeAllCookies();
// Check that the default cookie manager still does not have cookies.
Assert.assertFalse(defaultCookieManager.hasCookies());
webServer.shutdown();
}
@Test
@MediumTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testSeparateProfilesHaveSeparateRenderProcesses() throws Throwable {
mRule.startBrowserProcess();
final AwBrowserContext profile = mRule.getProfileSync("my-profile", true);
final AwContents firstAwContents = mRule.createAwContents();
final AwContents secondAwContents = mRule.createAwContents(profile);
TestWebServer webServer = TestWebServer.start();
String path = "/test.html";
String responseStr = "<html><head><title>TEST!</title></head><body>HELLO!</body></html>";
String url = webServer.setResponse(path, responseStr, new ArrayList<>());
mRule.loadUrlSync(firstAwContents, mContentsClient.getOnPageFinishedHelper(), url);
assertEquals(1, RenderProcessHostUtils.getCurrentRenderProcessCount());
mRule.loadUrlSync(secondAwContents, mContentsClient.getOnPageFinishedHelper(), url);
assertEquals(2, RenderProcessHostUtils.getCurrentRenderProcessCount());
webServer.shutdown();
}
@Test
@MediumTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testAwContentsWithSameProfileShareRenderProcess() throws Throwable {
mRule.startBrowserProcess();
final AwBrowserContext profile = mRule.getProfileSync("my-profile", true);
final AwContents firstAwContents = mRule.createAwContents(profile);
final AwContents secondAwContents = mRule.createAwContents(profile);
TestWebServer webServer = TestWebServer.start();
String path = "/test.html";
String responseStr = "<html><head><title>TEST!</title></head><body>HELLO!</body></html>";
String url = webServer.setResponse(path, responseStr, new ArrayList<>());
mRule.loadUrlSync(firstAwContents, mContentsClient.getOnPageFinishedHelper(), url);
assertEquals(1, RenderProcessHostUtils.getCurrentRenderProcessCount());
mRule.loadUrlSync(secondAwContents, mContentsClient.getOnPageFinishedHelper(), url);
assertEquals(1, RenderProcessHostUtils.getCurrentRenderProcessCount());
webServer.shutdown();
}
private void validateCookies(
AwCookieManager cookieManager, String url, String... expectedCookieNames) {
final String responseCookie = cookieManager.getCookie(url);
assertNotNull(responseCookie);
String[] cookies = responseCookie.split(";");
// Convert to sets, since Set#equals() hooks in nicely with assertEquals()
Set<String> foundCookieNamesSet = new HashSet<String>();
for (String cookie : cookies) {
foundCookieNamesSet.add(cookie.substring(0, cookie.indexOf("=")).trim());
}
Set<String> expectedCookieNamesSet =
new HashSet<String>(Arrays.asList(expectedCookieNames));
assertEquals(
"Found cookies list differs from expected list",
expectedCookieNamesSet,
foundCookieNamesSet);
}
@Test
@LargeTest
@OnlyRunIn(MULTI_PROCESS)
@Feature({"AndroidWebView"})
public void testInjectedJavascriptIsTransferredWhenProfileChanges() throws Throwable {
mRule.startBrowserProcess();
String listenerName = "injectedListener";
String startupScript = listenerName + ".postMessage('success');";
String[] injectDomains = {"*"};
final AwContents webView = mRule.createAwContents();
AwActivityTestRule.enableJavaScriptOnUiThread(webView);
CallbackHelper testDoneHelper = new CallbackHelper();
final WebMessageListener injectedListener =
(payload, topLevelOrigin, sourceOrigin, isMainFrame, jsReplyProxy, ports) -> {
Assert.assertEquals("success", payload.getAsString());
testDoneHelper.notifyCalled();
};
// Setup a message listener and a startup script to post on to the listener.
mRule.runOnUiThread(
() -> {
webView.addWebMessageListener(listenerName, injectDomains, injectedListener);
webView.addDocumentStartJavaScript(startupScript, injectDomains);
});
// Switch the profile after the JS objects have been injected, but before content is loaded.
AwBrowserContext otherProfile = mRule.getProfileSync("other-profile", true);
mRule.setBrowserContextSync(webView, otherProfile);
// Load content using the new Context.
try (TestWebServer server = TestWebServer.start()) {
server.setResponse("/", "hello, world", new ArrayList<>());
mRule.loadUrlSync(
webView, mContentsClient.getOnPageFinishedHelper(), server.getBaseUrl());
Assert.assertEquals(
"Injected listener was missing",
"true",
mRule.executeJavaScriptAndWaitForResult(
webView, mContentsClient, listenerName + " != null"));
}
// Wait for the test to run (see injectedListener above).
testDoneHelper.waitForFirst(
"Did not receive post message triggered by injected javascript");
}
}