77fb3704fd3c45b8f658d5914b9980b7880f691a
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / LoadUrlTest.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.test.suitebuilder.annotation.LargeTest;
8 import android.test.suitebuilder.annotation.SmallTest;
9 import android.util.Pair;
10
11 import junit.framework.Assert;
12
13 import org.apache.http.Header;
14 import org.apache.http.HttpRequest;
15 import org.chromium.android_webview.AwContents;
16 import org.chromium.android_webview.AwSettings;
17 import org.chromium.android_webview.test.util.CommonResources;
18 import org.chromium.android_webview.test.util.JSUtils;
19 import org.chromium.base.test.util.Feature;
20 import org.chromium.content.browser.LoadUrlParams;
21 import org.chromium.content.browser.test.util.CallbackHelper;
22 import org.chromium.content.browser.test.util.HistoryUtils;
23 import org.chromium.net.test.util.TestWebServer;
24
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.concurrent.TimeUnit;
31
32 /**
33  * Test suite for loadUrl().
34  */
35 public class LoadUrlTest extends AwTestBase {
36     @SmallTest
37     @Feature({"AndroidWebView"})
38     public void testDataUrl() throws Throwable {
39         final String expectedTitle = "dataUrlTest";
40         final String data =
41             "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>";
42
43         final TestAwContentsClient contentsClient = new TestAwContentsClient();
44         final AwTestContainerView testContainerView =
45                 createAwTestContainerViewOnMainSync(contentsClient);
46         final AwContents awContents = testContainerView.getAwContents();
47         loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(), data,
48                      "text/html", false);
49         assertEquals(expectedTitle, getTitleOnUiThread(awContents));
50     }
51
52     @SmallTest
53     @Feature({"AndroidWebView"})
54     public void testDataUrlBase64() throws Throwable {
55         final String expectedTitle = "dataUrlTestBase64";
56         final String data = "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" +
57                             "L2hlYWQ+PC9odG1sPg==";
58
59         final TestAwContentsClient contentsClient = new TestAwContentsClient();
60         final AwTestContainerView testContainerView =
61                 createAwTestContainerViewOnMainSync(contentsClient);
62         final AwContents awContents = testContainerView.getAwContents();
63         loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(), data,
64                      "text/html", true);
65         assertEquals(expectedTitle, getTitleOnUiThread(awContents));
66     }
67
68     @SmallTest
69     @Feature({"AndroidWebView"})
70     public void testDataUrlCharset() throws Throwable {
71         // Note that the \u00a3 (pound sterling) is the important character in the following
72         // string as it's not in the US_ASCII character set.
73         final String expectedTitle = "You win \u00a3100!";
74         final String data =
75             "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>";
76         final TestAwContentsClient contentsClient = new TestAwContentsClient();
77         final AwTestContainerView testContainerView =
78                 createAwTestContainerViewOnMainSync(contentsClient);
79         final AwContents awContents = testContainerView.getAwContents();
80         loadDataSyncWithCharset(awContents, contentsClient.getOnPageFinishedHelper(), data,
81                      "text/html", false, "UTF-8");
82         assertEquals(expectedTitle, getTitleOnUiThread(awContents));
83     }
84
85     /**
86      * Loads url on the UI thread and blocks until onPageFinished is called.
87      */
88     protected void loadUrlWithExtraHeadersSync(
89             final AwContents awContents,
90             CallbackHelper onPageFinishedHelper,
91             final String url,
92             final Map<String, String> extraHeaders) throws Throwable {
93         int currentCallCount = onPageFinishedHelper.getCallCount();
94         runTestOnUiThread(new Runnable() {
95             @Override
96             public void run() {
97                 LoadUrlParams params = new LoadUrlParams(url);
98                 params.setExtraHeaders(extraHeaders);
99                 awContents.loadUrl(params);
100             }
101         });
102         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
103                                              TimeUnit.MILLISECONDS);
104     }
105
106     private static List<Pair<String, String>> createHeadersList(String[] namesAndValues) {
107         List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
108         for (int i = 0; i < namesAndValues.length; i += 2)
109             result.add(Pair.create(namesAndValues[i], namesAndValues[i + 1]));
110         return result;
111     }
112
113     private static Map<String, String> createHeadersMap(String[] namesAndValues) {
114         Map<String, String> result = new HashMap<String, String>();
115         for (int i = 0; i < namesAndValues.length; i += 2)
116             result.put(namesAndValues[i], namesAndValues[i + 1]);
117         return result;
118     }
119
120     private void validateRequestHeaders(String[] refNamesAndValues,
121                                         HttpRequest request) {
122         for (int i = 0; i < refNamesAndValues.length; i += 2) {
123             Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]);
124             assertEquals(1, matchingHeaders.length);
125
126             Header header = matchingHeaders[0];
127             assertEquals(refNamesAndValues[i].toLowerCase(Locale.ENGLISH), header.getName());
128             assertEquals(refNamesAndValues[i + 1], header.getValue());
129         }
130     }
131
132     private void validateNoRequestHeaders(String[] refNamesAndValues,
133                                           HttpRequest request) {
134         for (int i = 0; i < refNamesAndValues.length; i += 2) {
135             Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]);
136             assertEquals(0, matchingHeaders.length);
137         }
138     }
139
140     @SmallTest
141     @Feature({"AndroidWebView"})
142     public void testLoadUrlWithExtraHeaders() throws Throwable {
143         final TestAwContentsClient contentsClient = new TestAwContentsClient();
144         final AwTestContainerView testContainerView =
145                 createAwTestContainerViewOnMainSync(contentsClient);
146         final AwContents awContents = testContainerView.getAwContents();
147
148         TestWebServer webServer = null;
149         try {
150             webServer = new TestWebServer(false);
151             final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
152             webServer.setResponseBase64(imagePath,
153                     CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
154             final String path = "/load_url_with_extra_headers_test.html";
155             final String url = webServer.setResponse(
156                     path,
157                     CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
158                     null);
159             String[] extraHeaders = {
160                 "X-ExtraHeaders1", "extra-header-data1",
161                 "x-extraHeaders2", "EXTRA-HEADER-DATA2"
162             };
163
164             loadUrlWithExtraHeadersSync(awContents,
165                                         contentsClient.getOnPageFinishedHelper(),
166                                         url,
167                                         createHeadersMap(extraHeaders));
168             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
169             // Verify that extra headers are only passed for the main resource.
170             validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(imagePath));
171         } finally {
172             if (webServer != null) webServer.shutdown();
173         }
174     }
175
176     @SmallTest
177     @Feature({"AndroidWebView"})
178     public void testNoOverridingOfExistingHeaders() throws Throwable {
179         final TestAwContentsClient contentsClient = new TestAwContentsClient();
180         final AwTestContainerView testContainerView =
181                 createAwTestContainerViewOnMainSync(contentsClient);
182         final AwContents awContents = testContainerView.getAwContents();
183
184         TestWebServer webServer = null;
185         try {
186             webServer = new TestWebServer(false);
187             final String path = "/no_overriding_of_existing_headers_test.html";
188             final String url = webServer.setResponse(
189                     path,
190                     "<html><body>foo</body></html>",
191                     null);
192             String[] extraHeaders = {
193                 "user-agent", "007"
194             };
195
196             loadUrlWithExtraHeadersSync(awContents,
197                                         contentsClient.getOnPageFinishedHelper(),
198                                         url,
199                                         createHeadersMap(extraHeaders));
200             Header[] matchingHeaders = webServer.getLastRequest(path).getHeaders(extraHeaders[0]);
201             assertEquals(1, matchingHeaders.length);
202             Header header = matchingHeaders[0];
203             assertEquals(extraHeaders[0].toLowerCase(Locale.ENGLISH),
204                     header.getName().toLowerCase(Locale.ENGLISH));
205             // Just check that the value is there, and it's not the one we provided.
206             assertTrue(header.getValue().length() > 0);
207             assertFalse(extraHeaders[1].equals(header.getValue()));
208         } finally {
209             if (webServer != null) webServer.shutdown();
210         }
211     }
212
213     @SmallTest
214     @Feature({"AndroidWebView"})
215     public void testReloadWithExtraHeaders() throws Throwable {
216         final TestAwContentsClient contentsClient = new TestAwContentsClient();
217         final AwTestContainerView testContainerView =
218                 createAwTestContainerViewOnMainSync(contentsClient);
219         final AwContents awContents = testContainerView.getAwContents();
220
221         TestWebServer webServer = null;
222         try {
223             webServer = new TestWebServer(false);
224             final String path = "/reload_with_extra_headers_test.html";
225             final String url = webServer.setResponse(path,
226                     "<html><body>foo</body></html>",
227                     createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
228             String[] extraHeaders = {
229                 "X-ExtraHeaders1", "extra-header-data1",
230                 "x-extraHeaders2", "EXTRA-HEADER-DATA2"
231             };
232
233             loadUrlWithExtraHeadersSync(awContents,
234                                         contentsClient.getOnPageFinishedHelper(),
235                                         url,
236                                         createHeadersMap(extraHeaders));
237             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
238
239             reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
240             assertEquals(2, webServer.getRequestCount(path));
241             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
242         } finally {
243             if (webServer != null) webServer.shutdown();
244         }
245     }
246
247     @SmallTest
248     @Feature({"AndroidWebView"})
249     public void testRedirectAndReloadWithExtraHeaders() throws Throwable {
250         final TestAwContentsClient contentsClient = new TestAwContentsClient();
251         final AwTestContainerView testContainerView =
252                 createAwTestContainerViewOnMainSync(contentsClient);
253         final AwContents awContents = testContainerView.getAwContents();
254
255         TestWebServer webServer = null;
256         try {
257             webServer = new TestWebServer(false);
258             final String path = "/redirect_and_reload_with_extra_headers_test.html";
259             final String url = webServer.setResponse(path,
260                     "<html><body>foo</body></html>",
261                     createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
262             final String redirectedPath = "/redirected.html";
263             final String redirectedUrl = webServer.setRedirect(redirectedPath, path);
264             String[] extraHeaders = {
265                 "X-ExtraHeaders1", "extra-header-data1",
266                 "x-extraHeaders2", "EXTRA-HEADER-DATA2"
267             };
268
269             loadUrlWithExtraHeadersSync(awContents,
270                                         contentsClient.getOnPageFinishedHelper(),
271                                         redirectedUrl,
272                                         createHeadersMap(extraHeaders));
273             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
274             validateRequestHeaders(extraHeaders, webServer.getLastRequest(redirectedPath));
275
276             // WebView will only reload the main page.
277             reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
278             assertEquals(2, webServer.getRequestCount(path));
279             // No extra headers. This is consistent with legacy behavior.
280             validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(path));
281         } finally {
282             if (webServer != null) webServer.shutdown();
283         }
284     }
285
286     @SmallTest
287     @Feature({"AndroidWebView"})
288     public void testRendererNavigationAndGoBackWithExtraHeaders() throws Throwable {
289         final TestAwContentsClient contentsClient = new TestAwContentsClient();
290         final AwTestContainerView testContainerView =
291                 createAwTestContainerViewOnMainSync(contentsClient);
292         final AwContents awContents = testContainerView.getAwContents();
293         final AwSettings settings = getAwSettingsOnUiThread(awContents);
294         settings.setJavaScriptEnabled(true);
295
296         TestWebServer webServer = null;
297         try {
298             webServer = new TestWebServer(false);
299             final String nextPath = "/next.html";
300             final String nextUrl = webServer.setResponse(nextPath,
301                     "<html><body>Next!</body></html>",
302                     null);
303             final String path = "/renderer_nav_and_go_back_with_extra_headers_test.html";
304             final String url = webServer.setResponse(path,
305                     "<html><body><a id=\"next\" href=\"next.html\">Next!</a></body></html>",
306                     createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
307             String[] extraHeaders = {
308                 "X-ExtraHeaders1", "extra-header-data1",
309                 "x-extraHeaders2", "EXTRA-HEADER-DATA2"
310             };
311
312             loadUrlWithExtraHeadersSync(awContents,
313                                         contentsClient.getOnPageFinishedHelper(),
314                                         url,
315                                         createHeadersMap(extraHeaders));
316             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
317
318             int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
319             JSUtils.clickOnLinkUsingJs(this,
320                                        awContents,
321                                        contentsClient.getOnEvaluateJavaScriptResultHelper(),
322                                        "next");
323             contentsClient.getOnPageFinishedHelper().waitForCallback(
324                     currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
325             // No extra headers for the page navigated via clicking.
326             validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(nextPath));
327
328             HistoryUtils.goBackSync(getInstrumentation(),
329                                     awContents.getContentViewCore(),
330                                     contentsClient.getOnPageFinishedHelper());
331             assertEquals(2, webServer.getRequestCount(path));
332             validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
333         } finally {
334             if (webServer != null) webServer.shutdown();
335         }
336     }
337
338     private static class TestController {
339         private final Object mLock = new Object();
340         private boolean mIsReady = false;
341         public void notifyPageIsReady() {
342             synchronized (mLock) {
343                 mIsReady = true;
344                 mLock.notify();
345             }
346         }
347         public void waitUntilIsReady() {
348             synchronized (mLock) {
349                 while (!mIsReady) {
350                     try {
351                         mLock.wait(WAIT_TIMEOUT_MS);
352                     } catch (Exception e) {
353                         continue;
354                     }
355                     if (!mIsReady) {
356                         Assert.fail("Wait timed out");
357                     }
358                 }
359                 mIsReady = false;
360             }
361         }
362     }
363
364     // Verify that it is possible to interrupt JS scripts stuck in an infinite loop
365     // by calling loadUrl on a WebView.
366     @LargeTest
367     @Feature({"AndroidWebView"})
368     public void testLoadUrlInterruptsLoopedScripts() throws Throwable {
369         final String infiniteLoopPage =
370             "<html><head>" +
371             "  <script>" +
372             "    function infiniteLoop() {" +
373             "      test.notifyPageIsReady();" +
374             "      while(1);" +
375             "    }" +
376             "  </script>" +
377             "</head><body onload='setTimeout(infiniteLoop, 0)'>" +
378             "</body></html>";
379         final String simplePage = "<html><body onload='test.notifyPageIsReady()'></body></html>";
380         final String expectedTitle = "PASS";
381         final String pageWithTitle = "<html><body onload='document.title=\"" + expectedTitle +
382                 "\"; test.notifyPageIsReady()'></body></html>";
383
384         final AwTestContainerView testContainerView =
385                 createAwTestContainerViewOnMainSync(new TestAwContentsClient());
386         final AwContents awContents = testContainerView.getAwContents();
387         getAwSettingsOnUiThread(awContents).setJavaScriptEnabled(true);
388         final TestController testController = new TestController();
389         getInstrumentation().runOnMainSync(new Runnable() {
390             @Override
391             public void run() {
392                 awContents.addPossiblyUnsafeJavascriptInterface(testController, "test", null);
393             }
394         });
395         loadDataAsync(awContents, infiniteLoopPage, "text/html", false);
396         testController.waitUntilIsReady();
397         loadDataAsync(awContents, simplePage, "text/html", false);
398         testController.waitUntilIsReady();
399         // Load another page that runs JS to make sure that the WebView is still functional.
400         loadDataAsync(awContents, pageWithTitle, "text/html", false);
401         testController.waitUntilIsReady();
402         assertEquals(expectedTitle, getTitleOnUiThread(awContents));
403     }
404 }