Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / WebKitHitTestTest.java
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.android_webview.test;
6
7 import android.os.Handler;
8 import android.os.Message;
9 import android.os.SystemClock;
10 import android.test.suitebuilder.annotation.LargeTest;
11 import android.test.suitebuilder.annotation.SmallTest;
12 import android.view.KeyEvent;
13 import android.view.MotionEvent;
14 import android.webkit.WebView.HitTestResult;
15
16 import org.chromium.android_webview.AwContents;
17 import org.chromium.android_webview.test.util.CommonResources;
18 import org.chromium.base.ThreadUtils;
19 import org.chromium.base.test.util.Feature;
20 import org.chromium.net.test.util.TestWebServer;
21
22 import java.util.concurrent.Callable;
23
24 /**
25  * Test for getHitTestResult, requestFocusNodeHref, and requestImageRef methods
26  */
27 public class WebKitHitTestTest extends AwTestBase {
28     private TestAwContentsClient mContentsClient;
29     private AwTestContainerView mTestView;
30     private AwContents mAwContents;
31     private TestWebServer mWebServer;
32
33     private static final String HREF = "http://foo/";
34     private static final String ANCHOR_TEXT = "anchor text";
35
36     @Override
37     public void setUp() throws Exception {
38         super.setUp();
39         mContentsClient = new TestAwContentsClient();
40         mTestView = createAwTestContainerViewOnMainSync(mContentsClient);
41         mAwContents = mTestView.getAwContents();
42         mWebServer = new TestWebServer(false);
43     }
44
45     @Override
46     public void tearDown() throws Exception {
47         if (mWebServer != null) {
48             mWebServer.shutdown();
49         }
50         super.tearDown();
51     }
52
53     private void setServerResponseAndLoad(String response) throws Throwable {
54         String url = mWebServer.setResponse("/hittest.html", response, null);
55         loadUrlSync(mAwContents,
56                     mContentsClient.getOnPageFinishedHelper(),
57                     url);
58     }
59
60     private static String fullPageLink(String href, String anchorText) {
61         return CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\"" +
62                 href + "\" " + "onclick=\"return false;\">" + anchorText + "</a>");
63     }
64
65     private void simulateTouchCenterOfWebViewOnUiThread() throws Throwable {
66         runTestOnUiThread(new Runnable() {
67             @Override
68             public void run() {
69                 long eventTime = SystemClock.uptimeMillis();
70                 float x = (float) (mTestView.getRight() - mTestView.getLeft()) / 2;
71                 float y = (float) (mTestView.getBottom() - mTestView.getTop()) / 2;
72                 mAwContents.onTouchEvent(MotionEvent.obtain(
73                         eventTime, eventTime, MotionEvent.ACTION_DOWN,
74                         x, y, 0));
75                 mAwContents.onTouchEvent(MotionEvent.obtain(
76                         eventTime, eventTime, MotionEvent.ACTION_UP,
77                         x, y, 0));
78             }
79         });
80     }
81
82     private void simulateTabDownUpOnUiThread() throws Throwable {
83         runTestOnUiThread(new Runnable() {
84             @Override
85             public void run() {
86                 mAwContents.getContentViewCore().dispatchKeyEvent(
87                         new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
88                 mAwContents.getContentViewCore().dispatchKeyEvent(
89                         new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TAB));
90             }
91         });
92     }
93
94     private void simulateInput(boolean byTouch) throws Throwable {
95         // Send a touch click event if byTouch is true. Otherwise, send a TAB
96         // key event to change the focused element of the page.
97         if (byTouch) {
98             simulateTouchCenterOfWebViewOnUiThread();
99         } else {
100             simulateTabDownUpOnUiThread();
101         }
102     }
103
104     private static boolean stringEquals(String a, String b) {
105         return a == null ? b == null : a.equals(b);
106     }
107
108     private void pollForHitTestDataOnUiThread(
109             final int expectedType, final String expectedExtra) throws Throwable {
110         pollOnUiThread(new Callable<Boolean>() {
111             @Override
112             public Boolean call() {
113                 AwContents.HitTestData data = mAwContents.getLastHitTestResult();
114                 return expectedType == data.hitTestResultType &&
115                        stringEquals(expectedExtra, data.hitTestResultExtraData);
116             }
117         });
118     }
119
120     private void pollForHrefAndImageSrcOnUiThread(
121             final String expectedHref,
122             final String expectedAnchorText,
123             final String expectedImageSrc) throws Throwable {
124         pollOnUiThread(new Callable<Boolean>() {
125             @Override
126             public Boolean call() {
127                 AwContents.HitTestData data = mAwContents.getLastHitTestResult();
128                 return stringEquals(expectedHref, data.href) &&
129                        stringEquals(expectedAnchorText, data.anchorText) &&
130                        stringEquals(expectedImageSrc, data.imgSrc);
131             }
132         });
133
134         Handler dummyHandler = new Handler();
135         final Message focusNodeHrefMsg = dummyHandler.obtainMessage();
136         final Message imageRefMsg = dummyHandler.obtainMessage();
137         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
138             @Override
139             public void run() {
140                 mAwContents.requestFocusNodeHref(focusNodeHrefMsg);
141                 mAwContents.requestImageRef(imageRefMsg);
142             }
143         });
144         assertEquals(expectedHref, focusNodeHrefMsg.getData().getString("url"));
145         assertEquals(expectedAnchorText, focusNodeHrefMsg.getData().getString("title"));
146         assertEquals(expectedImageSrc, focusNodeHrefMsg.getData().getString("src"));
147         assertEquals(expectedImageSrc, imageRefMsg.getData().getString("url"));
148     }
149
150     private void srcAnchorTypeTestBody(boolean byTouch) throws Throwable {
151         String page = fullPageLink(HREF, ANCHOR_TEXT);
152         setServerResponseAndLoad(page);
153         simulateInput(byTouch);
154         pollForHitTestDataOnUiThread(HitTestResult.SRC_ANCHOR_TYPE, HREF);
155         pollForHrefAndImageSrcOnUiThread(HREF, ANCHOR_TEXT, null);
156     }
157
158     @SmallTest
159     @Feature({"AndroidWebView", "WebKitHitTest"})
160     public void testSrcAnchorType() throws Throwable {
161         srcAnchorTypeTestBody(true);
162     }
163
164     @SmallTest
165     @Feature({"AndroidWebView", "WebKitHitTest"})
166     public void testSrcAnchorTypeByFocus() throws Throwable {
167         srcAnchorTypeTestBody(false);
168     }
169
170     private void blankHrefTestBody(boolean byTouch) throws Throwable {
171         String fullPath = mWebServer.getResponseUrl("/hittest.html");
172         String page = fullPageLink("", ANCHOR_TEXT);
173         setServerResponseAndLoad(page);
174         simulateInput(byTouch);
175         pollForHitTestDataOnUiThread(HitTestResult.SRC_ANCHOR_TYPE, fullPath);
176         pollForHrefAndImageSrcOnUiThread(fullPath, ANCHOR_TEXT, null);
177     }
178
179     @SmallTest
180     @Feature({"AndroidWebView", "WebKitHitTest"})
181     public void testSrcAnchorTypeBlankHref() throws Throwable {
182         blankHrefTestBody(true);
183     }
184
185     @SmallTest
186     @Feature({"AndroidWebView", "WebKitHitTest"})
187     public void testSrcAnchorTypeBlankHrefByFocus() throws Throwable {
188         blankHrefTestBody(false);
189     }
190
191     private void srcAnchorTypeRelativeUrlTestBody(boolean byTouch) throws Throwable {
192         String relPath = "/foo.html";
193         String fullPath = mWebServer.getResponseUrl(relPath);
194         String page = fullPageLink(relPath, ANCHOR_TEXT);
195         setServerResponseAndLoad(page);
196         simulateInput(byTouch);
197         pollForHitTestDataOnUiThread(HitTestResult.SRC_ANCHOR_TYPE, fullPath);
198         pollForHrefAndImageSrcOnUiThread(fullPath, ANCHOR_TEXT, null);
199     }
200
201     @SmallTest
202     @Feature({"AndroidWebView", "WebKitHitTest"})
203     public void testSrcAnchorTypeRelativeUrl() throws Throwable {
204         srcAnchorTypeRelativeUrlTestBody(true);
205     }
206
207     @SmallTest
208     @Feature({"AndroidWebView", "WebKitHitTest"})
209     public void testSrcAnchorTypeRelativeUrlByFocus() throws Throwable {
210         srcAnchorTypeRelativeUrlTestBody(false);
211     }
212
213     private void srcEmailTypeTestBody(boolean byTouch) throws Throwable {
214         String email = "foo@bar.com";
215         String prefix = "mailto:";
216         String page = fullPageLink(prefix + email, ANCHOR_TEXT);
217         setServerResponseAndLoad(page);
218         simulateInput(byTouch);
219         pollForHitTestDataOnUiThread(HitTestResult.EMAIL_TYPE, email);
220         pollForHrefAndImageSrcOnUiThread(prefix + email, ANCHOR_TEXT, null);
221     }
222
223     @SmallTest
224     @Feature({"AndroidWebView", "WebKitHitTest"})
225     public void testSrcEmailType() throws Throwable {
226         srcEmailTypeTestBody(true);
227     }
228
229     @SmallTest
230     @Feature({"AndroidWebView", "WebKitHitTest"})
231     public void testSrcEmailTypeByFocus() throws Throwable {
232         srcEmailTypeTestBody(false);
233     }
234
235     private void srcGeoTypeTestBody(boolean byTouch) throws Throwable {
236         String location = "Jilin";
237         String prefix = "geo:0,0?q=";
238         String page = fullPageLink(prefix + location, ANCHOR_TEXT);
239         setServerResponseAndLoad(page);
240         simulateInput(byTouch);
241         pollForHitTestDataOnUiThread(HitTestResult.GEO_TYPE, location);
242         pollForHrefAndImageSrcOnUiThread(prefix + location, ANCHOR_TEXT, null);
243     }
244
245     @SmallTest
246     @Feature({"AndroidWebView", "WebKitHitTest"})
247     public void testSrcGeoType() throws Throwable {
248         srcGeoTypeTestBody(true);
249     }
250
251     @SmallTest
252     @Feature({"AndroidWebView", "WebKitHitTest"})
253     public void testSrcGeoTypeByFocus() throws Throwable {
254         srcGeoTypeTestBody(false);
255     }
256
257     private void srcPhoneTypeTestBody(boolean byTouch) throws Throwable {
258         String phone_num = "%2B1234567890";
259         String expected_phone_num = "+1234567890";
260         String prefix = "tel:";
261         String page = fullPageLink("tel:" + phone_num, ANCHOR_TEXT);
262         setServerResponseAndLoad(page);
263         simulateInput(byTouch);
264         pollForHitTestDataOnUiThread(HitTestResult.PHONE_TYPE, expected_phone_num);
265         pollForHrefAndImageSrcOnUiThread(prefix + phone_num, ANCHOR_TEXT, null);
266     }
267
268     @SmallTest
269     @Feature({"AndroidWebView", "WebKitHitTest"})
270     public void testSrcPhoneType() throws Throwable {
271         srcPhoneTypeTestBody(true);
272     }
273
274     @SmallTest
275     @Feature({"AndroidWebView", "WebKitHitTest"})
276     public void testSrcPhoneTypeByFocus() throws Throwable {
277         srcPhoneTypeTestBody(false);
278     }
279
280     private void srcImgeAnchorTypeTestBody(boolean byTouch) throws Throwable {
281         String fullImageSrc = "http://foo.bar/nonexistent.jpg";
282         String page = CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\"" +
283                 HREF + "\"onclick=\"return false;\"><img class=\"full_view\" src=\"" +
284                 fullImageSrc + "\"></a>");
285         setServerResponseAndLoad(page);
286         simulateInput(byTouch);
287         pollForHitTestDataOnUiThread(HitTestResult.SRC_IMAGE_ANCHOR_TYPE, fullImageSrc);
288         pollForHrefAndImageSrcOnUiThread(HREF, null, fullImageSrc);
289     }
290
291     @SmallTest
292     @Feature({"AndroidWebView", "WebKitHitTest"})
293     public void testSrcImgeAnchorType() throws Throwable {
294         srcImgeAnchorTypeTestBody(true);
295     }
296
297     @SmallTest
298     @Feature({"AndroidWebView", "WebKitHitTest"})
299     public void testSrcImgeAnchorTypeByFocus() throws Throwable {
300         srcImgeAnchorTypeTestBody(false);
301     }
302
303     private void srcImgeAnchorTypeRelativeUrlTestBody(boolean byTouch) throws Throwable {
304         String relImageSrc = "/nonexistent.jpg";
305         String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
306         String relPath = "/foo.html";
307         String fullPath = mWebServer.getResponseUrl(relPath);
308         String page = CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\"" +
309                 relPath + "\"onclick=\"return false;\"><img class=\"full_view\" src=\"" +
310                 relImageSrc + "\"></a>");
311         setServerResponseAndLoad(page);
312         simulateInput(byTouch);
313         pollForHitTestDataOnUiThread(HitTestResult.SRC_IMAGE_ANCHOR_TYPE, fullImageSrc);
314         pollForHrefAndImageSrcOnUiThread(fullPath, null, fullImageSrc);
315     }
316
317     @SmallTest
318     @Feature({"AndroidWebView", "WebKitHitTest"})
319     public void testSrcImgeAnchorTypeRelativeUrl() throws Throwable {
320         srcImgeAnchorTypeRelativeUrlTestBody(true);
321     }
322
323     @SmallTest
324     @Feature({"AndroidWebView", "WebKitHitTest"})
325     public void testSrcImgeAnchorTypeRelativeUrlByFocus() throws Throwable {
326         srcImgeAnchorTypeRelativeUrlTestBody(false);
327     }
328
329     @SmallTest
330     @Feature({"AndroidWebView", "WebKitHitTest"})
331     public void testImgeType() throws Throwable {
332         String relImageSrc = "/nonexistent2.jpg";
333         String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
334         String page = CommonResources.makeHtmlPageFrom("",
335                 "<img class=\"full_view\" src=\"" + relImageSrc + "\">");
336         setServerResponseAndLoad(page);
337         simulateTouchCenterOfWebViewOnUiThread();
338         pollForHitTestDataOnUiThread(HitTestResult.IMAGE_TYPE, fullImageSrc);
339         pollForHrefAndImageSrcOnUiThread(null, null, fullImageSrc);
340     }
341
342     private void editTextTypeTestBody(boolean byTouch) throws Throwable {
343         String page = CommonResources.makeHtmlPageFrom("",
344                 "<form><input class=\"full_view\" type=\"text\" name=\"test\"></form>");
345         setServerResponseAndLoad(page);
346         simulateInput(byTouch);
347         pollForHitTestDataOnUiThread(HitTestResult.EDIT_TEXT_TYPE, null);
348         pollForHrefAndImageSrcOnUiThread(null, null, null);
349     }
350
351     @SmallTest
352     @Feature({"AndroidWebView", "WebKitHitTest"})
353     public void testEditTextType() throws Throwable {
354         editTextTypeTestBody(true);
355     }
356
357     @SmallTest
358     @Feature({"AndroidWebView", "WebKitHitTest"})
359     public void testEditTextTypeByFocus() throws Throwable {
360         editTextTypeTestBody(false);
361     }
362
363     public void unknownTypeJavascriptSchemeTestBody(boolean byTouch) throws Throwable {
364         // Per documentation, javascript urls are special.
365         String javascript = "javascript:alert('foo');";
366         String page = fullPageLink(javascript, ANCHOR_TEXT);
367         setServerResponseAndLoad(page);
368         simulateInput(byTouch);
369         pollForHrefAndImageSrcOnUiThread(javascript, ANCHOR_TEXT, null);
370         pollForHitTestDataOnUiThread(HitTestResult.UNKNOWN_TYPE, null);
371     }
372
373     @SmallTest
374     @Feature({"AndroidWebView", "WebKitHitTest"})
375     public void testUnknownTypeJavascriptScheme() throws Throwable {
376         unknownTypeJavascriptSchemeTestBody(true);
377     }
378
379     @SmallTest
380     @Feature({"AndroidWebView", "WebKitHitTest"})
381     public void testUnknownTypeJavascriptSchemeByFocus() throws Throwable {
382         unknownTypeJavascriptSchemeTestBody(false);
383     }
384
385     @SmallTest
386     @Feature({"AndroidWebView", "WebKitHitTest"})
387     public void testUnknownTypeUnrecognizedNode() throws Throwable {
388         // Since UNKNOWN_TYPE is the default, hit test another type first for
389         // this test to be valid.
390         testSrcAnchorType();
391
392         final String title = "UNKNOWN_TYPE title";
393
394         String page = CommonResources.makeHtmlPageFrom(
395                 "<title>" + title + "</title>",
396                 "<div class=\"full_view\">div text</div>");
397         setServerResponseAndLoad(page);
398
399         // Wait for the new page to be loaded before trying hit test.
400         pollOnUiThread(new Callable<Boolean>() {
401             @Override
402             public Boolean call() {
403                 return mAwContents.getContentViewCore().getTitle().equals(title);
404             }
405         });
406         simulateTouchCenterOfWebViewOnUiThread();
407         pollForHitTestDataOnUiThread(HitTestResult.UNKNOWN_TYPE, null);
408     }
409
410     @LargeTest
411     @Feature({"AndroidWebView", "WebKitHitTest"})
412     public void testUnfocusedNodeAndTouchRace() throws Throwable {
413         // Test when the touch and focus paths racing with setting different
414         // results.
415
416         String relImageSrc = "/nonexistent3.jpg";
417         String fullImageSrc = mWebServer.getResponseUrl(relImageSrc);
418         String html = CommonResources.makeHtmlPageFrom(
419                 "<meta name=\"viewport\" content=\"width=device-width,height=device-height\" />" +
420                         "<style type=\"text/css\">" +
421                         ".full_width { width:100%; position:absolute; }" +
422                         "</style>",
423                         "<form><input class=\"full_width\" style=\"height:25%;\" " +
424                         "type=\"text\" name=\"test\"></form>" +
425                         "<img class=\"full_width\" style=\"height:50%;top:25%;\" " +
426                         "src=\"" + relImageSrc + "\">");
427         setServerResponseAndLoad(html);
428
429         // Focus on input element and check the hit test results.
430         simulateTabDownUpOnUiThread();
431         pollForHitTestDataOnUiThread(HitTestResult.EDIT_TEXT_TYPE, null);
432         pollForHrefAndImageSrcOnUiThread(null, null, null);
433
434         // Touch image. Now the focus based hit test path will try to null out
435         // the results and the touch based path will update with the result of
436         // the image.
437         simulateTouchCenterOfWebViewOnUiThread();
438
439         // Make sure the result of image sticks.
440         for (int i = 0; i < 2; ++i) {
441             Thread.sleep(500);
442             pollForHitTestDataOnUiThread(HitTestResult.IMAGE_TYPE, fullImageSrc);
443             pollForHrefAndImageSrcOnUiThread(null, null, fullImageSrc);
444         }
445     }
446 }