blob: 3998d725494b09c4faf2d060558d4113a6542d37 [file] [log] [blame]
// Copyright 2012 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.os.SystemClock;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.webkit.WebView.HitTestResult;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.test.util.CommonResources;
import org.chromium.base.test.util.Feature;
import org.chromium.net.test.util.TestWebServer;
import java.util.concurrent.Callable;
/**
* Test for getHitTestResult, requestFocusNodeHref, and requestImageRef methods
*/
public class WebKitHitTestTest extends AwTestBase {
private TestAwContentsClient mContentsClient;
private AwTestContainerView mTestView;
private AwContents mAwContents;
private TestWebServer mWebServer;
private static String HREF = "http://foo/";
private static String ANCHOR_TEXT = "anchor text";
@Override
public void setUp() throws Exception {
super.setUp();
mContentsClient = new TestAwContentsClient();
mTestView = createAwTestContainerViewOnMainSync(mContentsClient);
mAwContents = mTestView.getAwContents();
mWebServer = new TestWebServer(false);
}
@Override
public void tearDown() throws Exception {
if (mWebServer != null) {
mWebServer.shutdown();
}
super.tearDown();
}
private void setServerResponseAndLoad(String response) throws Throwable {
String url = mWebServer.setResponse("/hittest.html", response, null);
loadUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(),
url);
}
private static String fullPageLink(String href, String anchorText) {
return CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\"" +
href + "\" " + "onclick=\"return false;\">" + anchorText + "</a>");
}
private void simulateTouchCenterOfWebViewOnUiThread() throws Throwable {
runTestOnUiThread(new Runnable() {
@Override
public void run() {
long eventTime = SystemClock.uptimeMillis();
float x = (float) (mTestView.getRight() - mTestView.getLeft()) / 2;
float y = (float) (mTestView.getBottom() - mTestView.getTop()) / 2;
mAwContents.onTouchEvent(MotionEvent.obtain(
eventTime, eventTime, MotionEvent.ACTION_DOWN,
x, y, 0));
mAwContents.onTouchEvent(MotionEvent.obtain(
eventTime, eventTime, MotionEvent.ACTION_UP,
x, y, 0));
}
});
}
private void simulateTabDownUpOnUiThread() throws Throwable {
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mAwContents.getContentViewCore().dispatchKeyEvent(
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
mAwContents.getContentViewCore().dispatchKeyEvent(
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TAB));
}
});
}
private void simulateInput(boolean byTouch) throws Throwable {
// Send a touch click event if byTouch is true. Otherwise, send a TAB
// key event to change the focused element of the page.
if (byTouch) {
simulateTouchCenterOfWebViewOnUiThread();
} else {
simulateTabDownUpOnUiThread();
}
}
private boolean pollForHitTestDataOnUiThread(
final int type, final String extra) throws Throwable {
return pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
AwContents.HitTestData data = mAwContents.getLastHitTestResult();
return type == data.hitTestResultType &&
(extra == null ? data.hitTestResultExtraData == null :
extra.equals(data.hitTestResultExtraData));
}
});
}
private boolean pollForHrefAndImageSrcOnUiThread(
final String href,
final String anchorText,
final String imageSrc) throws Throwable {
return pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
AwContents.HitTestData data = mAwContents.getLastHitTestResult();
return (href == null ? data.href == null :
href.equals(data.href)) &&
(anchorText == null ? data.anchorText == null :
anchorText.equals(data.anchorText)) &&
(imageSrc == null ? data.imgSrc == null :
imageSrc.equals(data.imgSrc));
}
});
}
private void srcAnchorTypeTestBody(boolean byTouch) throws Throwable {
String page = fullPageLink(HREF, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.SRC_ANCHOR_TYPE, HREF));
assertTrue(pollForHrefAndImageSrcOnUiThread(HREF, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorType() throws Throwable {
srcAnchorTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorTypeByFocus() throws Throwable {
srcAnchorTypeTestBody(false);
}
private void blankHrefTestBody(boolean byTouch) throws Throwable {
String fullpath = mWebServer.getResponseUrl("/hittest.html");
String page = fullPageLink("", ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.SRC_ANCHOR_TYPE, fullpath));
assertTrue(pollForHrefAndImageSrcOnUiThread(fullpath, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorTypeBlankHref() throws Throwable {
blankHrefTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorTypeBlankHrefByFocus() throws Throwable {
blankHrefTestBody(false);
}
private void srcAnchorTypeRelativeUrlTestBody(boolean byTouch) throws Throwable {
String relpath = "/foo.html";
String fullpath = mWebServer.getResponseUrl(relpath);
String page = fullPageLink(relpath, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.SRC_ANCHOR_TYPE, fullpath));
assertTrue(pollForHrefAndImageSrcOnUiThread(fullpath, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorTypeRelativeUrl() throws Throwable {
srcAnchorTypeRelativeUrlTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcAnchorTypeRelativeUrlByFocus() throws Throwable {
srcAnchorTypeRelativeUrlTestBody(false);
}
private void srcEmailTypeTestBody(boolean byTouch) throws Throwable {
String email = "foo@bar.com";
String prefix = "mailto:";
String page = fullPageLink(prefix + email, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.EMAIL_TYPE, email));
assertTrue(pollForHrefAndImageSrcOnUiThread(prefix + email, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcEmailType() throws Throwable {
srcEmailTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcEmailTypeByFocus() throws Throwable {
srcEmailTypeTestBody(false);
}
private void srcGeoTypeTestBody(boolean byTouch) throws Throwable {
String location = "Jilin";
String prefix = "geo:0,0?q=";
String page = fullPageLink(prefix + location, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.GEO_TYPE, location));
assertTrue(pollForHrefAndImageSrcOnUiThread(prefix + location, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcGeoType() throws Throwable {
srcGeoTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcGeoTypeByFocus() throws Throwable {
srcGeoTypeTestBody(false);
}
private void srcPhoneTypeTestBody(boolean byTouch) throws Throwable {
String phone_num = "%2B1234567890";
String expected_phone_num = "+1234567890";
String prefix = "tel:";
String page = fullPageLink("tel:" + phone_num, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.PHONE_TYPE, expected_phone_num));
assertTrue(pollForHrefAndImageSrcOnUiThread(prefix + phone_num, ANCHOR_TEXT, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcPhoneType() throws Throwable {
srcPhoneTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcPhoneTypeByFocus() throws Throwable {
srcPhoneTypeTestBody(false);
}
private void srcImgeAnchorTypeTestBody(boolean byTouch) throws Throwable {
String relImageSrc = "/nonexistent.jpg";
String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
String page = CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\"" +
HREF + "\"onclick=\"return false;\"><img class=\"full_view\" src=\"" +
relImageSrc + "\"></a>");
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.SRC_IMAGE_ANCHOR_TYPE, fullImageSrc));
assertTrue(pollForHrefAndImageSrcOnUiThread(HREF, null, fullImageSrc));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcImgeAnchorType() throws Throwable {
srcImgeAnchorTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testSrcImgeAnchorTypeByFocus() throws Throwable {
srcImgeAnchorTypeTestBody(false);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testImgeType() throws Throwable {
String relImageSrc = "/nonexistent2.jpg";
String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
String page = CommonResources.makeHtmlPageFrom("",
"<img class=\"full_view\" src=\"" + relImageSrc + "\">");
setServerResponseAndLoad(page);
simulateTouchCenterOfWebViewOnUiThread();
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.IMAGE_TYPE, fullImageSrc));
assertTrue(pollForHrefAndImageSrcOnUiThread(null, null, fullImageSrc));
}
private void editTextTypeTestBody(boolean byTouch) throws Throwable {
String page = CommonResources.makeHtmlPageFrom("",
"<form><input class=\"full_view\" type=\"text\" name=\"test\"></form>");
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.EDIT_TEXT_TYPE, null));
assertTrue(pollForHrefAndImageSrcOnUiThread(null, null, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testEditTextType() throws Throwable {
editTextTypeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testEditTextTypeByFocus() throws Throwable {
editTextTypeTestBody(false);
}
public void unknownTypeJavascriptSchemeTestBody(boolean byTouch) throws Throwable {
// Per documentation, javascript urls are special.
String javascript = "javascript:alert('foo');";
String page = fullPageLink(javascript, ANCHOR_TEXT);
setServerResponseAndLoad(page);
simulateInput(byTouch);
assertTrue(pollForHrefAndImageSrcOnUiThread(javascript, ANCHOR_TEXT, null));
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.UNKNOWN_TYPE, null));
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testUnknownTypeJavascriptScheme() throws Throwable {
unknownTypeJavascriptSchemeTestBody(true);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testUnknownTypeJavascriptSchemeByFocus() throws Throwable {
unknownTypeJavascriptSchemeTestBody(false);
}
@SmallTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testUnknownTypeUnrecognizedNode() throws Throwable {
// Since UNKNOWN_TYPE is the default, hit test another type first for
// this test to be valid.
testSrcAnchorType();
final String title = "UNKNOWN_TYPE title";
String page = CommonResources.makeHtmlPageFrom(
"<title>" + title + "</title>",
"<div class=\"full_view\">div text</div>");
setServerResponseAndLoad(page);
// Wait for the new page to be loaded before trying hit test.
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getContentViewCore().getTitle().equals(title);
}
});
simulateTouchCenterOfWebViewOnUiThread();
assertTrue(pollForHitTestDataOnUiThread(HitTestResult.UNKNOWN_TYPE, null));
}
@LargeTest
@Feature({"AndroidWebView", "WebKitHitTest"})
public void testUnfocusedNodeAndTouchRace() throws Throwable {
// Test when the touch and focus paths racing with setting different
// results.
String relImageSrc = "/nonexistent3.jpg";
String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
String html = CommonResources.makeHtmlPageFrom(
"<meta name=\"viewport\" content=\"width=device-width,height=device-height\" />" +
"<style type=\"text/css\">" +
".full_width { width:100%; position:absolute; }" +
"</style>",
"<form><input class=\"full_width\" style=\"height:25%;\" " +
"type=\"text\" name=\"test\"></form>" +
"<img class=\"full_width\" style=\"height:50%;top:25%;\" " +
"src=\"" + relImageSrc + "\">");
setServerResponseAndLoad(html);
// Focus on input element and check the hit test results.
simulateTabDownUpOnUiThread();
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.EDIT_TEXT_TYPE, null));
assertTrue(pollForHrefAndImageSrcOnUiThread(null, null, null));
// Touch image. Now the focus based hit test path will try to null out
// the results and the touch based path will update with the result of
// the image.
simulateTouchCenterOfWebViewOnUiThread();
// Make sure the result of image sticks.
for (int i = 0; i < 2; ++i) {
Thread.sleep(500);
assertTrue(pollForHitTestDataOnUiThread(
HitTestResult.IMAGE_TYPE, fullImageSrc));
assertTrue(pollForHrefAndImageSrcOnUiThread(null, null, fullImageSrc));
}
}
}