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.
5 package org.chromium.android_webview.test;
7 import android.test.suitebuilder.annotation.LargeTest;
8 import android.test.suitebuilder.annotation.SmallTest;
9 import android.util.Pair;
11 import junit.framework.Assert;
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;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
30 import java.util.concurrent.TimeUnit;
33 * Test suite for loadUrl().
35 public class LoadUrlTest extends AwTestBase {
37 @Feature({"AndroidWebView"})
38 public void testDataUrl() throws Throwable {
39 final String expectedTitle = "dataUrlTest";
41 "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>";
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,
49 assertEquals(expectedTitle, getTitleOnUiThread(awContents));
53 @Feature({"AndroidWebView"})
54 public void testDataUrlBase64() throws Throwable {
55 final String expectedTitle = "dataUrlTestBase64";
56 final String data = "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" +
57 "L2hlYWQ+PC9odG1sPg==";
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,
65 assertEquals(expectedTitle, getTitleOnUiThread(awContents));
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!";
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));
86 * Loads url on the UI thread and blocks until onPageFinished is called.
88 protected void loadUrlWithExtraHeadersSync(
89 final AwContents awContents,
90 CallbackHelper onPageFinishedHelper,
92 final Map<String, String> extraHeaders) throws Throwable {
93 int currentCallCount = onPageFinishedHelper.getCallCount();
94 runTestOnUiThread(new Runnable() {
97 LoadUrlParams params = new LoadUrlParams(url);
98 params.setExtraHeaders(extraHeaders);
99 awContents.loadUrl(params);
102 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
103 TimeUnit.MILLISECONDS);
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]));
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]);
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);
126 Header header = matchingHeaders[0];
127 assertEquals(refNamesAndValues[i].toLowerCase(Locale.ENGLISH), header.getName());
128 assertEquals(refNamesAndValues[i + 1], header.getValue());
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);
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();
148 TestWebServer webServer = null;
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(
157 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
159 String[] extraHeaders = {
160 "X-ExtraHeaders1", "extra-header-data1",
161 "x-extraHeaders2", "EXTRA-HEADER-DATA2"
164 loadUrlWithExtraHeadersSync(awContents,
165 contentsClient.getOnPageFinishedHelper(),
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));
172 if (webServer != null) webServer.shutdown();
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();
184 TestWebServer webServer = null;
186 webServer = new TestWebServer(false);
187 final String path = "/no_overriding_of_existing_headers_test.html";
188 final String url = webServer.setResponse(
190 "<html><body>foo</body></html>",
192 String[] extraHeaders = {
196 loadUrlWithExtraHeadersSync(awContents,
197 contentsClient.getOnPageFinishedHelper(),
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()));
209 if (webServer != null) webServer.shutdown();
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();
221 TestWebServer webServer = null;
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"
233 loadUrlWithExtraHeadersSync(awContents,
234 contentsClient.getOnPageFinishedHelper(),
236 createHeadersMap(extraHeaders));
237 validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
239 reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
240 assertEquals(2, webServer.getRequestCount(path));
241 validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
243 if (webServer != null) webServer.shutdown();
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();
255 TestWebServer webServer = null;
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"
269 loadUrlWithExtraHeadersSync(awContents,
270 contentsClient.getOnPageFinishedHelper(),
272 createHeadersMap(extraHeaders));
273 validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
274 validateRequestHeaders(extraHeaders, webServer.getLastRequest(redirectedPath));
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));
282 if (webServer != null) webServer.shutdown();
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);
296 TestWebServer webServer = null;
298 webServer = new TestWebServer(false);
299 final String nextPath = "/next.html";
300 final String nextUrl = webServer.setResponse(nextPath,
301 "<html><body>Next!</body></html>",
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"
312 loadUrlWithExtraHeadersSync(awContents,
313 contentsClient.getOnPageFinishedHelper(),
315 createHeadersMap(extraHeaders));
316 validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
318 int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
319 JSUtils.clickOnLinkUsingJs(this,
321 contentsClient.getOnEvaluateJavaScriptResultHelper(),
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));
328 HistoryUtils.goBackSync(getInstrumentation(),
329 awContents.getContentViewCore(),
330 contentsClient.getOnPageFinishedHelper());
331 assertEquals(2, webServer.getRequestCount(path));
332 validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
334 if (webServer != null) webServer.shutdown();
338 private static class TestController {
339 private final Object mLock = new Object();
340 private boolean mIsReady = false;
341 public void notifyPageIsReady() {
342 synchronized (mLock) {
347 public void waitUntilIsReady() {
348 synchronized (mLock) {
351 mLock.wait(WAIT_TIMEOUT_MS);
352 } catch (Exception e) {
356 Assert.fail("Wait timed out");
364 // Verify that it is possible to interrupt JS scripts stuck in an infinite loop
365 // by calling loadUrl on a WebView.
367 @Feature({"AndroidWebView"})
368 public void testLoadUrlInterruptsLoopedScripts() throws Throwable {
369 final String infiniteLoopPage =
372 " function infiniteLoop() {" +
373 " test.notifyPageIsReady();" +
377 "</head><body onload='setTimeout(infiniteLoop, 0)'>" +
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>";
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() {
392 awContents.addPossiblyUnsafeJavascriptInterface(testController, "test", null);
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));