1 // Copyright (c) 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.AndroidProtocolHandler;
11 import org.chromium.android_webview.AwContents;
12 import org.chromium.android_webview.AwSettings;
13 import org.chromium.android_webview.test.util.CommonResources;
14 import org.chromium.base.test.util.DisabledTest;
15 import org.chromium.base.test.util.Feature;
16 import org.chromium.content.browser.ContentViewCore;
17 import org.chromium.content.browser.LoadUrlParams;
18 import org.chromium.content.browser.test.util.Criteria;
19 import org.chromium.content.browser.test.util.CriteriaHelper;
20 import org.chromium.content.browser.test.util.HistoryUtils;
21 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
22 import org.chromium.net.test.util.TestWebServer;
25 import java.io.FileOutputStream;
26 import java.util.concurrent.TimeUnit;
28 public class LoadDataWithBaseUrlTest extends AwTestBase {
30 private TestAwContentsClient mContentsClient;
31 private AwContents mAwContents;
32 private ContentViewCore mContentViewCore;
35 public void setUp() throws Exception {
37 mContentsClient = new TestAwContentsClient();
38 final AwTestContainerView testContainerView =
39 createAwTestContainerViewOnMainSync(mContentsClient);
40 mAwContents = testContainerView.getAwContents();
41 mContentViewCore = testContainerView.getContentViewCore();
44 protected void loadDataWithBaseUrlSync(
45 final String data, final String mimeType, final boolean isBase64Encoded,
46 final String baseUrl, final String historyUrl) throws Throwable {
47 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
48 mContentsClient.getOnPageFinishedHelper();
49 int currentCallCount = onPageFinishedHelper.getCallCount();
50 loadDataWithBaseUrlAsync(data, mimeType, isBase64Encoded, baseUrl, historyUrl);
51 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS,
55 protected void loadDataWithBaseUrlAsync(
56 final String data, final String mimeType, final boolean isBase64Encoded,
57 final String baseUrl, final String historyUrl) throws Throwable {
58 runTestOnUiThread(new Runnable() {
61 mAwContents.loadUrl(LoadUrlParams.createLoadDataParamsWithBaseUrl(
62 data, mimeType, isBase64Encoded, baseUrl, historyUrl));
67 private static final String SCRIPT_FILE = "/script.js";
68 private static final String SCRIPT_LOADED = "Loaded";
69 private static final String SCRIPT_NOT_LOADED = "Not loaded";
70 private static final String SCRIPT_JS = "script_was_loaded = true;";
72 private String getScriptFileTestPageHtml(final String scriptUrl) {
75 " <title>" + SCRIPT_NOT_LOADED + "</title>" +
76 " <script src='" + scriptUrl + "'></script>" +
78 " <body onload=\"if(script_was_loaded) document.title='" + SCRIPT_LOADED + "'\">" +
83 private String getCrossOriginAccessTestPageHtml(final String iframeUrl) {
87 " function onload() {" +
89 " document.title = " +
90 " document.getElementById('frame').contentWindow.location.href;" +
92 " document.title = 'Exception';" +
97 " <body onload='onload()'>" +
98 " <iframe id='frame' src='" + iframeUrl + "'></iframe>" +
105 @Feature({"AndroidWebView"})
106 public void testImageLoad() throws Throwable {
107 TestWebServer webServer = null;
109 webServer = new TestWebServer(false);
110 webServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME,
111 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
113 AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
114 contentSettings.setImagesEnabled(true);
115 contentSettings.setJavaScriptEnabled(true);
117 loadDataWithBaseUrlSync(
118 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
119 "text/html", false, webServer.getBaseUrl(), null);
121 assertEquals("5", getTitleOnUiThread(mAwContents));
123 if (webServer != null) webServer.shutdown();
128 @Feature({"AndroidWebView"})
129 public void testScriptLoad() throws Throwable {
130 TestWebServer webServer = null;
132 webServer = new TestWebServer(false);
134 final String scriptUrl = webServer.setResponse(SCRIPT_FILE, SCRIPT_JS,
135 CommonResources.getTextJavascriptHeaders(true));
136 final String pageHtml = getScriptFileTestPageHtml(scriptUrl);
138 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
139 loadDataWithBaseUrlSync(pageHtml, "text/html", false, webServer.getBaseUrl(), null);
140 assertEquals(SCRIPT_LOADED, getTitleOnUiThread(mAwContents));
143 if (webServer != null) webServer.shutdown();
148 @Feature({"AndroidWebView"})
149 public void testSameOrigin() throws Throwable {
150 TestWebServer webServer = null;
152 webServer = new TestWebServer(false);
153 final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
154 CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
155 final String html = getCrossOriginAccessTestPageHtml(frameUrl);
157 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
158 loadDataWithBaseUrlSync(html, "text/html", false, webServer.getBaseUrl(), null);
159 assertEquals(frameUrl, getTitleOnUiThread(mAwContents));
162 if (webServer != null) webServer.shutdown();
167 @Feature({"AndroidWebView"})
168 public void testCrossOrigin() throws Throwable {
169 TestWebServer webServer = null;
171 webServer = new TestWebServer(false);
172 final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
173 CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
174 final String html = getCrossOriginAccessTestPageHtml(frameUrl);
175 final String baseUrl = webServer.getBaseUrl().replaceFirst("localhost", "127.0.0.1");
177 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
178 loadDataWithBaseUrlSync(html, "text/html", false, baseUrl, null);
180 assertEquals("Exception", getTitleOnUiThread(mAwContents));
183 if (webServer != null) webServer.shutdown();
188 @Feature({"AndroidWebView"})
189 public void testNullBaseUrl() throws Throwable {
190 getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
191 final String pageHtml = "<html><body onload='document.title=document.location.href'>" +
193 loadDataWithBaseUrlSync(pageHtml, "text/html", false, null, null);
194 assertEquals("about:blank", getTitleOnUiThread(mAwContents));
198 @Feature({"AndroidWebView"})
199 public void testHistoryUrl() throws Throwable {
201 final String pageHtml = "<html><body>Hello, world!</body></html>";
202 final String baseUrl = "http://example.com";
203 // TODO(mnaganov): Use the same string as Android CTS suite uses
204 // once GURL issue is resolved (http://code.google.com/p/google-url/issues/detail?id=29)
205 final String historyUrl = "http://history.com/";
206 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, historyUrl);
207 assertEquals(historyUrl, HistoryUtils.getUrlOnUiThread(
208 getInstrumentation(), mContentViewCore));
210 loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, null);
211 assertEquals("about:blank", HistoryUtils.getUrlOnUiThread(
212 getInstrumentation(), mContentViewCore));
216 @Feature({"AndroidWebView"})
217 public void testHistoryUrlIgnoredWithDataSchemeBaseUrl() throws Throwable {
218 final String pageHtml = "<html><body>bar</body></html>";
219 final String historyUrl = "http://history.com/";
220 loadDataWithBaseUrlSync(pageHtml, "text/html", false, "data:foo", historyUrl);
221 assertEquals("data:text/html," + pageHtml, HistoryUtils.getUrlOnUiThread(
222 getInstrumentation(), mContentViewCore));
227 @Feature({"AndroidWebView"})
228 http://crbug.com/173274
231 public void testHistoryUrlNavigation() throws Throwable {
232 TestWebServer webServer = null;
234 webServer = new TestWebServer(false);
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(), mContentViewCore, onPageFinishedHelper);
255 // The title of the 'about.html' specified via historyUrl.
256 assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
259 if (webServer != null) webServer.shutdown();
264 * @return true if |fileUrl| was accessible from a data url with |baseUrl| as it's
267 private boolean canAccessFileFromData(String baseUrl, String fileUrl) throws Throwable {
268 final String IMAGE_LOADED = "LOADED";
269 final String IMAGE_NOT_LOADED = "NOT_LOADED";
270 String data = "<html><body>" +
271 "<img src=\"" + fileUrl + "\" " +
272 "onload=\"document.title=\'" + IMAGE_LOADED + "\';\" " +
273 "onerror=\"document.title=\'" + IMAGE_NOT_LOADED + "\';\" />" +
276 loadDataWithBaseUrlSync(data, "text/html", false, baseUrl, null);
278 CriteriaHelper.pollForCriteria(new Criteria() {
280 public boolean isSatisfied() {
282 String title = getTitleOnUiThread(mAwContents);
283 return IMAGE_LOADED.equals(title) || IMAGE_NOT_LOADED.equals(title);
284 } catch (Throwable t) {
290 return IMAGE_LOADED.equals(getTitleOnUiThread(mAwContents));
294 @Feature({"AndroidWebView"})
295 public void testLoadDataWithBaseUrlAccessingFile() throws Throwable {
296 // Create a temporary file on the filesystem we can try to read.
297 File cacheDir = getActivity().getCacheDir();
298 File tempImage = File.createTempFile("test_image", ".png", cacheDir);
299 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
300 FileOutputStream fos = new FileOutputStream(tempImage);
301 bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
303 String imagePath = tempImage.getAbsolutePath();
305 AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
306 contentSettings.setImagesEnabled(true);
307 contentSettings.setJavaScriptEnabled(true);
310 final String DATA_BASE_URL = "data:";
311 final String NON_DATA_BASE_URL = "http://example.com";
313 mAwContents.getSettings().setAllowFileAccess(false);
314 String token = "" + System.currentTimeMillis();
315 // All access to file://, including android_asset and android_res is blocked
316 // with a data: base URL, regardless of AwSettings.getAllowFileAccess().
317 assertFalse(canAccessFileFromData(DATA_BASE_URL,
318 "file:///android_asset/asset_icon.png?" + token));
319 assertFalse(canAccessFileFromData(DATA_BASE_URL,
320 "file:///android_res/raw/resource_icon.png?" + token));
321 assertFalse(canAccessFileFromData(DATA_BASE_URL, "file://" + imagePath + "?" + token));
323 // WebView always has access to android_asset and android_res for non-data
324 // base URLs and can access other file:// URLs based on the value of
325 // AwSettings.getAllowFileAccess().
326 assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
327 "file:///android_asset/asset_icon.png?" + token));
328 assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
329 "file:///android_res/raw/resource_icon.png?" + token));
330 assertFalse(canAccessFileFromData(NON_DATA_BASE_URL,
331 "file://" + imagePath + "?" + token));
334 mAwContents.getSettings().setAllowFileAccess(true);
335 // We should still be unable to access any file:// with when loading with a
336 // data: base URL, but we should now be able to access the wider file system
337 // (still restricted by OS-level permission checks) with a non-data base URL.
338 assertFalse(canAccessFileFromData(DATA_BASE_URL,
339 "file:///android_asset/asset_icon.png?" + token));
340 assertFalse(canAccessFileFromData(DATA_BASE_URL,
341 "file:///android_res/raw/resource_icon.png?" + token));
342 assertFalse(canAccessFileFromData(DATA_BASE_URL, "file://" + imagePath + "?" + token));
344 assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
345 "file:///android_asset/asset_icon.png?" + token));
346 assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
347 "file:///android_res/raw/resource_icon.png?" + token));
348 assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
349 "file://" + imagePath + "?" + token));
351 if (!tempImage.delete()) throw new AssertionError();