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