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.graphics.Bitmap;
8 import android.test.suitebuilder.annotation.SmallTest;
10 import org.chromium.android_webview.AwContents;
11 import org.chromium.android_webview.AwSettings;
12 import org.chromium.android_webview.test.util.CommonResources;
13 import org.chromium.base.test.util.DisabledTest;
14 import org.chromium.base.test.util.Feature;
15 import org.chromium.content.browser.test.util.HistoryUtils;
16 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
17 import org.chromium.content_public.browser.WebContents;
18 import org.chromium.net.test.util.TestWebServer;
21 import java.io.FileOutputStream;
22 import java.util.concurrent.Callable;
25 * Tests for the {@link android.webkit.WebView#loadDataWithBaseURL(String, String, String, String,
28 public class LoadDataWithBaseUrlTest extends AwTestBase {
30 private TestAwContentsClient mContentsClient;
31 private AwContents mAwContents;
32 private WebContents mWebContents;
35 public void setUp() throws Exception {
37 mContentsClient = new TestAwContentsClient();
38 final AwTestContainerView testContainerView =
39 createAwTestContainerViewOnMainSync(mContentsClient);
40 mAwContents = testContainerView.getAwContents();
41 mWebContents = mAwContents.getWebContents();
44 protected void loadDataWithBaseUrlSync(
45 final String data, final String mimeType, final boolean isBase64Encoded,
46 final String baseUrl, final String historyUrl) throws Throwable {
47 loadDataWithBaseUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
48 data, mimeType, isBase64Encoded, baseUrl, historyUrl);
51 private static final String SCRIPT_FILE = "/script.js";
52 private static final String SCRIPT_LOADED = "Loaded";
53 private static final String SCRIPT_NOT_LOADED = "Not loaded";
54 private static final String SCRIPT_JS = "script_was_loaded = true;";
56 private String getScriptFileTestPageHtml(final String scriptUrl) {
59 " <title>" + SCRIPT_NOT_LOADED + "</title>" +
60 " <script src='" + scriptUrl + "'></script>" +
62 " <body onload=\"if(script_was_loaded) document.title='" + SCRIPT_LOADED + "'\">" +
67 private String getCrossOriginAccessTestPageHtml(final String iframeUrl) {
71 " function onload() {" +
73 " document.title = " +
74 " document.getElementById('frame').contentWindow.location.href;" +
76 " document.title = 'Exception';" +
81 " <body onload='onload()'>" +
82 " <iframe id='frame' src='" + iframeUrl + "'></iframe>" +
89 @Feature({"AndroidWebView"})
90 public void testImageLoad() throws Throwable {
91 TestWebServer webServer = TestWebServer.start();
93 webServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME,
94 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
96 AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
97 contentSettings.setImagesEnabled(true);
98 contentSettings.setJavaScriptEnabled(true);
100 loadDataWithBaseUrlSync(
101 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
102 "text/html", false, webServer.getBaseUrl(), null);
104 assertEquals("5", getTitleOnUiThread(mAwContents));
106 webServer.shutdown();
111 @Feature({"AndroidWebView"})
112 public void testScriptLoad() throws Throwable {
113 TestWebServer webServer = TestWebServer.start();
115 final String scriptUrl = webServer.setResponse(SCRIPT_FILE, SCRIPT_JS,
116 CommonResources.getTextJavascriptHeaders(true));
117 final String pageHtml = getScriptFileTestPageHtml(scriptUrl);
119 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
120 loadDataWithBaseUrlSync(pageHtml, "text/html", false, webServer.getBaseUrl(), null);
121 assertEquals(SCRIPT_LOADED, getTitleOnUiThread(mAwContents));
123 webServer.shutdown();
128 @Feature({"AndroidWebView"})
129 public void testSameOrigin() throws Throwable {
130 TestWebServer webServer = TestWebServer.start();
132 final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
133 CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
134 final String html = getCrossOriginAccessTestPageHtml(frameUrl);
136 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
137 loadDataWithBaseUrlSync(html, "text/html", false, webServer.getBaseUrl(), null);
138 assertEquals(frameUrl, getTitleOnUiThread(mAwContents));
140 webServer.shutdown();
145 @Feature({"AndroidWebView"})
146 public void testCrossOrigin() throws Throwable {
147 TestWebServer webServer = TestWebServer.start();
149 final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
150 CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
151 final String html = getCrossOriginAccessTestPageHtml(frameUrl);
152 final String baseUrl = webServer.getBaseUrl().replaceFirst("localhost", "127.0.0.1");
154 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
155 loadDataWithBaseUrlSync(html, "text/html", false, baseUrl, null);
157 assertEquals("Exception", getTitleOnUiThread(mAwContents));
159 webServer.shutdown();
164 @Feature({"AndroidWebView"})
165 public void testNullBaseUrl() throws Throwable {
166 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
167 final String pageHtml = "<html><body onload='document.title=document.location.href'>" +
169 loadDataWithBaseUrlSync(pageHtml, "text/html", false, null, null);
170 assertEquals("about:blank", getTitleOnUiThread(mAwContents));
174 @Feature({"AndroidWebView"})
175 public void testloadDataWithBaseUrlCallsOnPageStarted() throws Throwable {
176 final String baseUrl = "http://base.com/";
177 TestCallbackHelperContainer.OnPageStartedHelper onPageStartedHelper =
178 mContentsClient.getOnPageStartedHelper();
179 final int callCount = onPageStartedHelper.getCallCount();
180 loadDataWithBaseUrlAsync(mAwContents, CommonResources.ABOUT_HTML, "text/html", false,
181 baseUrl, "about:blank");
182 onPageStartedHelper.waitForCallback(callCount);
183 assertEquals(baseUrl, onPageStartedHelper.getUrl());
187 @Feature({"AndroidWebView"})
188 public void testHistoryUrl() throws Throwable {
190 final String pageHtml = "<html><body>Hello, world!</body></html>";
191 final String baseUrl = "http://example.com";
192 // TODO(mnaganov): Use the same string as Android CTS suite uses
193 // once GURL issue is resolved (http://code.google.com/p/google-url/issues/detail?id=29)
194 final String historyUrl = "http://history.com/";
195 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, historyUrl);
196 assertEquals(historyUrl, HistoryUtils.getUrlOnUiThread(
197 getInstrumentation(), mWebContents));
199 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, null);
200 assertEquals("about:blank", HistoryUtils.getUrlOnUiThread(
201 getInstrumentation(), mWebContents));
205 @Feature({"AndroidWebView"})
206 public void testOnPageFinishedUrlIsBaseUrl() throws Throwable {
207 final String pageHtml = "<html><body>Hello, world!</body></html>";
208 final String baseUrl = "http://example.com/";
209 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
210 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
211 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
212 mContentsClient.getOnPageFinishedHelper();
213 assertEquals(baseUrl, onPageFinishedHelper.getUrl());
217 @Feature({"AndroidWebView"})
218 public void testHistoryUrlIgnoredWithDataSchemeBaseUrl() throws Throwable {
219 final String pageHtml = "<html><body>bar</body></html>";
220 final String historyUrl = "http://history.com/";
221 loadDataWithBaseUrlSync(pageHtml, "text/html", false, "data:foo", historyUrl);
222 assertEquals("data:text/html," + pageHtml, HistoryUtils.getUrlOnUiThread(
223 getInstrumentation(), mWebContents));
228 @Feature({"AndroidWebView"})
229 http://crbug.com/173274
232 public void testHistoryUrlNavigation() throws Throwable {
233 TestWebServer webServer = TestWebServer.start();
235 final String historyUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
236 CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
238 final String page1Title = "Page1";
239 final String page1Html = "<html><head><title>" + page1Title + "</title>" +
240 "<body>" + page1Title + "</body></html>";
242 loadDataWithBaseUrlSync(page1Html, "text/html", false, null, historyUrl);
243 assertEquals(page1Title, getTitleOnUiThread(mAwContents));
245 final String page2Title = "Page2";
246 final String page2Html = "<html><head><title>" + page2Title + "</title>" +
247 "<body>" + page2Title + "</body></html>";
249 final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
250 mContentsClient.getOnPageFinishedHelper();
251 loadDataSync(mAwContents, onPageFinishedHelper, page2Html, "text/html", false);
252 assertEquals(page2Title, getTitleOnUiThread(mAwContents));
254 HistoryUtils.goBackSync(getInstrumentation(), mWebContents, onPageFinishedHelper);
255 // The title of the 'about.html' specified via historyUrl.
256 assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
258 webServer.shutdown();
263 * @return true if |fileUrl| was accessible from a data url with |baseUrl| as it's
266 private boolean canAccessFileFromData(String baseUrl, String fileUrl) throws Throwable {
267 final String imageLoaded = "LOADED";
268 final String imageNotLoaded = "NOT_LOADED";
269 String data = "<html><body>" +
270 "<img src=\"" + fileUrl + "\" " +
271 "onload=\"document.title=\'" + imageLoaded + "\';\" " +
272 "onerror=\"document.title=\'" + imageNotLoaded + "\';\" />" +
275 loadDataWithBaseUrlSync(data, "text/html", false, baseUrl, null);
277 poll(new Callable<Boolean>() {
279 public Boolean call() throws Exception {
280 String title = getTitleOnUiThread(mAwContents);
281 return imageLoaded.equals(title) || imageNotLoaded.equals(title);
285 return imageLoaded.equals(getTitleOnUiThread(mAwContents));
289 @Feature({"AndroidWebView"})
290 public void testLoadDataWithBaseUrlAccessingFile() throws Throwable {
291 // Create a temporary file on the filesystem we can try to read.
292 File cacheDir = getActivity().getCacheDir();
293 File tempImage = File.createTempFile("test_image", ".png", cacheDir);
294 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
295 FileOutputStream fos = new FileOutputStream(tempImage);
296 bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
298 String imagePath = tempImage.getAbsolutePath();
300 AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
301 contentSettings.setImagesEnabled(true);
302 contentSettings.setJavaScriptEnabled(true);
305 final String dataBaseUrl = "data:";
306 final String nonDataBaseUrl = "http://example.com";
308 mAwContents.getSettings().setAllowFileAccess(false);
309 String token = "" + System.currentTimeMillis();
310 // All access to file://, including android_asset and android_res is blocked
311 // with a data: base URL, regardless of AwSettings.getAllowFileAccess().
312 assertFalse(canAccessFileFromData(dataBaseUrl,
313 "file:///android_asset/asset_icon.png?" + token));
314 assertFalse(canAccessFileFromData(dataBaseUrl,
315 "file:///android_res/raw/resource_icon.png?" + token));
316 assertFalse(canAccessFileFromData(dataBaseUrl, "file://" + imagePath + "?" + token));
318 // WebView always has access to android_asset and android_res for non-data
319 // base URLs and can access other file:// URLs based on the value of
320 // AwSettings.getAllowFileAccess().
321 assertTrue(canAccessFileFromData(nonDataBaseUrl,
322 "file:///android_asset/asset_icon.png?" + token));
323 assertTrue(canAccessFileFromData(nonDataBaseUrl,
324 "file:///android_res/raw/resource_icon.png?" + token));
325 assertFalse(canAccessFileFromData(nonDataBaseUrl,
326 "file://" + imagePath + "?" + token));
329 mAwContents.getSettings().setAllowFileAccess(true);
330 // We should still be unable to access any file:// with when loading with a
331 // data: base URL, but we should now be able to access the wider file system
332 // (still restricted by OS-level permission checks) with a non-data base URL.
333 assertFalse(canAccessFileFromData(dataBaseUrl,
334 "file:///android_asset/asset_icon.png?" + token));
335 assertFalse(canAccessFileFromData(dataBaseUrl,
336 "file:///android_res/raw/resource_icon.png?" + token));
337 assertFalse(canAccessFileFromData(dataBaseUrl, "file://" + imagePath + "?" + token));
339 assertTrue(canAccessFileFromData(nonDataBaseUrl,
340 "file:///android_asset/asset_icon.png?" + token));
341 assertTrue(canAccessFileFromData(nonDataBaseUrl,
342 "file:///android_res/raw/resource_icon.png?" + token));
343 assertTrue(canAccessFileFromData(nonDataBaseUrl,
344 "file://" + imagePath + "?" + token));
346 if (!tempImage.delete()) throw new AssertionError();