blob: 0720906aeedfb64a7d72f6ec8e4051e7b0603e2d [file] [log] [blame]
// Copyright 2013 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.content.Context;
import android.support.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
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.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwGeolocationPermissions;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.test.AwActivityTestRule.TestDependencyFactory;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.device.geolocation.LocationProviderOverrider;
import org.chromium.device.geolocation.MockLocationProvider;
import java.util.concurrent.Callable;
/**
* Test suite for Geolocation in AwContents. Smoke tests for
* basic functionality, and tests to ensure the AwContents.onPause
* and onResume APIs affect Geolocation as expected.
*/
@RunWith(AwJUnit4ClassRunner.class)
public class GeolocationTest {
@Rule
public AwActivityTestRule mActivityTestRule = new AwActivityTestRule() {
@Override
public TestDependencyFactory createTestDependencyFactory() {
return mOverridenFactory == null ? new TestDependencyFactory() : mOverridenFactory;
}
};
private TestAwContentsClient mContentsClient;
private AwContents mAwContents;
private MockLocationProvider mMockLocationProvider;
private TestDependencyFactory mOverridenFactory;
private static final String RAW_HTML =
"<!DOCTYPE html>\n"
+ "<html>\n"
+ " <head>\n"
+ " <title>Geolocation</title>\n"
+ " <script>\n"
+ " var positionCount = 0;\n"
+ " function gotPos(position) {\n"
+ " positionCount++;\n"
+ " }\n"
+ " function errorCallback(error){"
+ " window.document.title = 'deny';"
+ " console.log('navigator.getCurrentPosition error: ', error);"
+ " }"
+ " function initiate_getCurrentPosition() {\n"
+ " navigator.geolocation.getCurrentPosition(\n"
+ " gotPos, errorCallback, { });\n"
+ " }\n"
+ " function initiate_watchPosition() {\n"
+ " navigator.geolocation.watchPosition(\n"
+ " gotPos, errorCallback, { });\n"
+ " }\n"
+ " </script>\n"
+ " </head>\n"
+ " <body>\n"
+ " </body>\n"
+ "</html>";
private static class GrantPermisionAwContentClient extends TestAwContentsClient {
@Override
public void onGeolocationPermissionsShowPrompt(
String origin, AwGeolocationPermissions.Callback callback) {
callback.invoke(origin, true, true);
}
}
private static class DefaultPermisionAwContentClient extends TestAwContentsClient {
@Override
public void onGeolocationPermissionsShowPrompt(
String origin, AwGeolocationPermissions.Callback callback) {
// This method is empty intentionally to simulate callback is not referenced.
}
}
private void initAwContents(TestAwContentsClient contentsClient) {
mContentsClient = contentsClient;
mAwContents = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient)
.getAwContents();
AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> mAwContents.getSettings().setGeolocationEnabled(true));
}
@Before
public void setUp() {
mMockLocationProvider = new MockLocationProvider();
LocationProviderOverrider.setLocationProviderImpl(mMockLocationProvider);
}
@After
public void tearDown() {
mMockLocationProvider.stopUpdates();
mOverridenFactory = null;
}
private int getPositionCountFromJS() {
int result = -1;
try {
result = Integer.parseInt(mActivityTestRule.executeJavaScriptAndWaitForResult(
mAwContents, mContentsClient, "positionCount"));
} catch (Exception e) {
Assert.fail("Unable to get positionCount");
}
return result;
}
private void ensureGeolocationRunning(final boolean running) {
AwActivityTestRule.pollInstrumentationThread(
() -> mMockLocationProvider.isRunning() == running);
}
private static class GeolocationOnInsecureOriginsTestDependencyFactory
extends TestDependencyFactory {
private boolean mAllow;
public GeolocationOnInsecureOriginsTestDependencyFactory(boolean allow) {
mAllow = allow;
}
@Override
public AwSettings createAwSettings(Context context, boolean supportLegacyQuirks) {
return new AwSettings(context, false /* isAccessFromFileURLsGrantedByDefault */,
supportLegacyQuirks, false /* allowEmptyDocumentPersistence */, mAllow,
false /* doNotUpdateSelectionOnMutatingSelectionRange */);
}
}
/**
* Ensure that a call to navigator.getCurrentPosition works in WebView.
*/
@Test
@MediumTest
@Feature({"AndroidWebView"})
public void testGetPosition() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests(
"initiate_getCurrentPosition();", null));
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() == 1);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests(
"initiate_getCurrentPosition();", null));
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() == 2);
}
/**
* Ensure that a call to navigator.watchPosition works in WebView.
*/
@Test
@MediumTest
@Feature({"AndroidWebView"})
public void testWatchPosition() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
}
@Test
@MediumTest
@Feature({"AndroidWebView"})
public void testPauseGeolocationOnPause() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
// Start a watch going.
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
ensureGeolocationRunning(true);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onPause());
ensureGeolocationRunning(false);
try {
mActivityTestRule.executeJavaScriptAndWaitForResult(
mAwContents, mContentsClient, "positionCount = 0");
} catch (Exception e) {
Assert.fail("Unable to clear positionCount");
}
Assert.assertEquals(0, getPositionCountFromJS());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onResume());
ensureGeolocationRunning(true);
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
}
@Test
@MediumTest
@Feature({"AndroidWebView"})
public void testPauseAwContentsBeforeNavigating() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onPause());
// Start a watch going.
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
Assert.assertEquals(0, getPositionCountFromJS());
ensureGeolocationRunning(false);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onResume());
ensureGeolocationRunning(true);
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
}
@Test
@MediumTest
@Feature({"AndroidWebView"})
public void testResumeWhenNotStarted() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onPause());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mAwContents.onResume());
ensureGeolocationRunning(false);
}
@Test
@Feature({"AndroidWebView"})
@SmallTest
public void testDenyAccessByDefault() throws Throwable {
initAwContents(new DefaultPermisionAwContentClient());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests(
"initiate_getCurrentPosition();", null));
AwActivityTestRule.pollInstrumentationThread(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Runtime.getRuntime().gc();
return "deny".equals(mActivityTestRule.getTitleOnUiThread(mAwContents));
}
});
}
@Test
@Feature({"AndroidWebView"})
@SmallTest
public void testDenyOnInsecureOrigins() throws Throwable {
mOverridenFactory = new GeolocationOnInsecureOriginsTestDependencyFactory(false);
initAwContents(new GrantPermisionAwContentClient());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"http://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests(
"initiate_getCurrentPosition();", null));
AwActivityTestRule.pollInstrumentationThread(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Runtime.getRuntime().gc();
return "deny".equals(mActivityTestRule.getTitleOnUiThread(mAwContents));
}
});
}
@Test
@Feature({"AndroidWebView"})
@SmallTest
public void testAllowOnInsecureOriginsByDefault() throws Throwable {
initAwContents(new GrantPermisionAwContentClient());
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
"http://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
TestThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.evaluateJavaScriptForTests(
"initiate_getCurrentPosition();", null));
AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 0);
}
}