[go: nahoru, domu]

blob: daa12d37494c1bed17b8d658f036d42c13dcf48b [file] [log] [blame]
// Copyright 2022 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 android.webkit.WebSettings;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
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.AwContents;
import org.chromium.android_webview.AwServiceWorkerSettings;
import org.chromium.android_webview.ManifestMetadataUtil;
import org.chromium.base.Log;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
import org.chromium.net.test.util.TestWebServer;
import java.util.Collections;
import java.util.Set;
/**
* Test service worker settings APIs.
*
* These tests are functionally duplicates of the ones in {@link AwSettingsTest},
* and serve to ensure that service worker settings are applied, even if no
* {@link android.webkit.ServiceWorkerClient} is supplied.
*/
@RunWith(Parameterized.class)
@UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
@Batch(Batch.PER_CLASS)
public class AwServiceWorkerSettingsTest extends AwParameterizedTest {
public static final String TAG = "AwSWSettingsTest";
@Rule public AwActivityTestRule mActivityTestRule;
private TestWebServer mWebServer;
private TestAwContentsClient mContentsClient;
private AwTestContainerView mTestContainerView;
private AwContents mAwContents;
private AwServiceWorkerSettings mAwServiceWorkerSettings;
public static final String INDEX_URL = "/index.html";
public static final String SW_URL = "/sw.js";
public static final String FETCH_URL = "/content.txt";
private static final String INDEX_HTML_TEMPLATE =
"<!DOCTYPE html>\n"
+ "<script>\n"
+ " state = '';\n"
+ " function setState(newState) {\n"
+ " console.log(newState);\n"
+ " state = newState;\n"
+ " }\n"
+ " function swReady(sw) {\n"
+ " setState('sw_ready');\n"
+ " sw.postMessage({fetches: %d});\n" // <- Format param on this line
+ " }\n"
+ " navigator.serviceWorker.register('sw.js')\n"
+ " .then(sw_reg => {\n"
+ " setState('sw_registered');\n"
+ " let sw = sw_reg.installing || sw_reg.waiting || sw_reg.active;\n"
+ " if (sw.state == 'activated') {\n"
+ " swReady(sw);\n"
+ " } else {\n"
+ " sw.addEventListener('statechange', e => {\n"
+ " if(e.target.state == 'activated') swReady(e.target); \n"
+ " }); \n"
+ " }\n"
+ " }).catch(err => {\n"
+ " console.log(err);\n"
+ " setState('sw_registration_error');\n"
+ " });\n"
+ " navigator.serviceWorker.addEventListener('message',\n"
+ " event => setState(event.data.msg));\n"
+ " setState('page_loaded');\n"
+ "</script>\n";
private static final String NETWORK_ACCESS_SW_JS =
"self.addEventListener('message', async event => {\n"
+ " try {\n"
+ " let resp;\n"
+ " for (let i = 0; i < event.data.fetches; i++) {\n"
+ " resp = await fetch('content.txt');\n"
+ " }\n"
+ " if (resp && resp.ok) {\n"
+ " event.source.postMessage({ msg: await resp.text() });\n"
+ " } else {\n"
+ " event.source.postMessage({ msg: 'fetch_not_ok' });\n"
+ " }\n"
+ " } catch {\n"
+ " event.source.postMessage({ msg: 'fetch_catch' })\n"
+ " }\n"
+ "});\n";
private static final String FETCH_CONTENT = "fetch_success";
public AwServiceWorkerSettingsTest(AwSettingsMutation param) {
this.mActivityTestRule = new AwActivityTestRule(param.getMutation());
}
@Before
public void setUp() throws Exception {
mWebServer = TestWebServer.start();
}
/**
* Initialize test fields.
* Extracted to separate method instead of {@code setUp} to allow certain tests
* to configure an ApplicationContext before startup
*/
private void initAwServiceWorkerSettings() {
mContentsClient = new TestAwContentsClient();
mTestContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
mAwContents = mTestContainerView.getAwContents();
AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
mAwServiceWorkerSettings =
mActivityTestRule
.getAwBrowserContext()
.getServiceWorkerController()
.getAwServiceWorkerSettings();
// To ensure that any settings supplied by the user are respected, even if the
// serviceWorkerClient is null, we set it explicitly here.
// See http://crbug.com/979321
mActivityTestRule
.getAwBrowserContext()
.getServiceWorkerController()
.setServiceWorkerClient(null);
}
@After
public void tearDown() {
if (mWebServer != null) mWebServer.shutdown();
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testBlockNetworkLoadsFalse() throws Throwable {
initAwServiceWorkerSettings();
final String fullIndexUrl = mWebServer.setResponse(INDEX_URL, indexHtml(1), null);
mWebServer.setResponse(SW_URL, NETWORK_ACCESS_SW_JS, null);
mWebServer.setResponse(FETCH_URL, FETCH_CONTENT, null);
mAwServiceWorkerSettings.setBlockNetworkLoads(false);
loadPage(fullIndexUrl, FETCH_CONTENT);
Assert.assertEquals(1, mWebServer.getRequestCount(SW_URL));
Assert.assertEquals(
"The service worker should make one network request",
1,
mWebServer.getRequestCount(FETCH_URL));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testBlockNetworkLoadsTrue() throws Throwable {
initAwServiceWorkerSettings();
final String fullIndexUrl = mWebServer.setResponse(INDEX_URL, indexHtml(1), null);
mWebServer.setResponse(SW_URL, NETWORK_ACCESS_SW_JS, null);
mWebServer.setResponse(FETCH_URL, FETCH_CONTENT, null);
mAwServiceWorkerSettings.setBlockNetworkLoads(true);
// With network turned off, we do not expect to be able to load the sw.js, and registration
// will fail
loadPage(fullIndexUrl, "sw_registration_error");
Assert.assertEquals(
"The service worker should not be loaded", 0, mWebServer.getRequestCount(SW_URL));
Assert.assertEquals(
"The service worker should not make any network requests",
0,
mWebServer.getRequestCount(FETCH_URL));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testCacheModeLoadNoCache() throws Throwable {
initAwServiceWorkerSettings();
final String fullIndexUrl = mWebServer.setResponse(INDEX_URL, indexHtml(2), null);
mWebServer.setResponse(SW_URL, NETWORK_ACCESS_SW_JS, null);
mWebServer.setResponse(FETCH_URL, FETCH_CONTENT, null);
mAwServiceWorkerSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
loadPage(fullIndexUrl, FETCH_CONTENT);
Assert.assertEquals(
"Two requests should be made in no-cache mode",
2,
mWebServer.getRequestCount(FETCH_URL));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testCacheModeLoadCacheElseNetwork() throws Throwable {
initAwServiceWorkerSettings();
final String fullIndexUrl = mWebServer.setResponse(INDEX_URL, indexHtml(2), null);
mWebServer.setResponse(SW_URL, NETWORK_ACCESS_SW_JS, null);
mWebServer.setResponse(FETCH_URL, FETCH_CONTENT, null);
mAwServiceWorkerSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
loadPage(fullIndexUrl, FETCH_CONTENT);
Assert.assertEquals(
"Only one request should be made when cache is available",
1,
mWebServer.getRequestCount(FETCH_URL));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testCacheModeLoadCacheOnly() throws Throwable {
initAwServiceWorkerSettings();
final String fullIndexUrl = mWebServer.setResponse(INDEX_URL, indexHtml(2), null);
mWebServer.setResponse(SW_URL, NETWORK_ACCESS_SW_JS, null);
mWebServer.setResponse(FETCH_URL, FETCH_CONTENT, null);
mAwServiceWorkerSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
// sw won't be in cache so register will fail
loadPage(fullIndexUrl, "sw_registration_error");
Assert.assertEquals(
"No requests should be made in cache-only mode",
0,
mWebServer.getRequestCount(SW_URL));
Assert.assertEquals(
"No requests should be made in cache-only mode",
0,
mWebServer.getRequestCount(FETCH_URL));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
public void testGetUpdatedXRWAllowList() throws Throwable {
initAwServiceWorkerSettings();
final Set<String> allowList = Set.of("https://*.example.com", "https://*.google.com");
Assert.assertEquals(
Collections.emptySet(),
mAwServiceWorkerSettings.getRequestedWithHeaderOriginAllowList());
mAwServiceWorkerSettings.setRequestedWithHeaderOriginAllowList(allowList);
Assert.assertEquals(
allowList, mAwServiceWorkerSettings.getRequestedWithHeaderOriginAllowList());
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "ServiceWorker"})
@CommandLineFlags.Add({"enable-features=WebViewXRequestedWithHeaderManifestAllowList"})
public void testXRequestedWithAllowListSetByManifest() throws Throwable {
final Set<String> allowList = Set.of("https://*.example.com", "https://*.google.com");
try (var a = ManifestMetadataUtil.setXRequestedWithAllowListScopedForTesting(allowList)) {
// Only initialize once the manifest has been configured
initAwServiceWorkerSettings();
Set<String> changedList =
mAwServiceWorkerSettings.getRequestedWithHeaderOriginAllowList();
Assert.assertEquals(allowList, changedList);
}
}
private String indexHtml(int fetches) {
return String.format(INDEX_HTML_TEMPLATE, fetches);
}
private void loadPage(final String fullIndexUrl, String expectedState) throws Exception {
TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
mContentsClient.getOnPageFinishedHelper();
mActivityTestRule.loadUrlSync(mAwContents, onPageFinishedHelper, fullIndexUrl);
Assert.assertEquals(fullIndexUrl, onPageFinishedHelper.getUrl());
String expectedAsJson = "\"" + expectedState + "\"";
AwActivityTestRule.pollInstrumentationThread(() -> expectedAsJson.equals(getStateFromJs()));
}
private String getStateFromJs() throws Exception {
String state =
mActivityTestRule.executeJavaScriptAndWaitForResult(
mAwContents, mContentsClient, "state");
// Logging the state helps with troubleshooting
Log.i(TAG, "state = %s", state);
return state;
}
}