- add sources.
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / LoadDataWithBaseUrlTest.java
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.
4
5 package org.chromium.android_webview.test;
6
7 import android.graphics.Bitmap;
8 import android.test.suitebuilder.annotation.SmallTest;
9
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;
23
24 import java.io.File;
25 import java.io.FileOutputStream;
26 import java.util.concurrent.TimeUnit;
27
28 public class LoadDataWithBaseUrlTest extends AwTestBase {
29
30     private TestAwContentsClient mContentsClient;
31     private AwContents mAwContents;
32     private ContentViewCore mContentViewCore;
33
34     @Override
35     public void setUp() throws Exception {
36         super.setUp();
37         mContentsClient = new TestAwContentsClient();
38         final AwTestContainerView testContainerView =
39                 createAwTestContainerViewOnMainSync(mContentsClient);
40         mAwContents = testContainerView.getAwContents();
41         mContentViewCore = testContainerView.getContentViewCore();
42     }
43
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,
52                 TimeUnit.SECONDS);
53     }
54
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() {
59             @Override
60             public void run() {
61                 mAwContents.loadUrl(LoadUrlParams.createLoadDataParamsWithBaseUrl(
62                         data, mimeType, isBase64Encoded, baseUrl, historyUrl));
63             }
64         });
65     }
66
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;";
71
72     private String getScriptFileTestPageHtml(final String scriptUrl) {
73         return "<html>" +
74                 "  <head>" +
75                 "    <title>" + SCRIPT_NOT_LOADED + "</title>" +
76                 "    <script src='" + scriptUrl + "'></script>" +
77                 "  </head>" +
78                 "  <body onload=\"if(script_was_loaded) document.title='" + SCRIPT_LOADED + "'\">" +
79                 "  </body>" +
80                 "</html>";
81     }
82
83     private String getCrossOriginAccessTestPageHtml(final String iframeUrl) {
84         return "<html>" +
85                 "  <head>" +
86                 "    <script>" +
87                 "      function onload() {" +
88                 "        try {" +
89                 "          document.title = " +
90                 "            document.getElementById('frame').contentWindow.location.href;" +
91                 "        } catch (e) {" +
92                 "          document.title = 'Exception';" +
93                 "        }" +
94                 "      }" +
95                 "    </script>" +
96                 "  </head>" +
97                 "  <body onload='onload()'>" +
98                 "    <iframe id='frame' src='" + iframeUrl + "'></iframe>" +
99                 "  </body>" +
100                 "</html>";
101     }
102
103
104     @SmallTest
105     @Feature({"AndroidWebView"})
106     public void testImageLoad() throws Throwable {
107         TestWebServer webServer = null;
108         try {
109             webServer = new TestWebServer(false);
110             webServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME,
111                     CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
112
113             AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
114             contentSettings.setImagesEnabled(true);
115             contentSettings.setJavaScriptEnabled(true);
116
117             loadDataWithBaseUrlSync(
118                     CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
119                     "text/html", false, webServer.getBaseUrl(), null);
120
121             assertEquals("5", getTitleOnUiThread(mAwContents));
122         } finally {
123             if (webServer != null) webServer.shutdown();
124         }
125     }
126
127     @SmallTest
128     @Feature({"AndroidWebView"})
129     public void testScriptLoad() throws Throwable {
130         TestWebServer webServer = null;
131         try {
132             webServer = new TestWebServer(false);
133
134             final String scriptUrl = webServer.setResponse(SCRIPT_FILE, SCRIPT_JS,
135                     CommonResources.getTextJavascriptHeaders(true));
136             final String pageHtml = getScriptFileTestPageHtml(scriptUrl);
137
138             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
139             loadDataWithBaseUrlSync(pageHtml, "text/html", false, webServer.getBaseUrl(), null);
140             assertEquals(SCRIPT_LOADED, getTitleOnUiThread(mAwContents));
141
142         } finally {
143             if (webServer != null) webServer.shutdown();
144         }
145     }
146
147     @SmallTest
148     @Feature({"AndroidWebView"})
149     public void testSameOrigin() throws Throwable {
150         TestWebServer webServer = null;
151         try {
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);
156
157             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
158             loadDataWithBaseUrlSync(html, "text/html", false, webServer.getBaseUrl(), null);
159             assertEquals(frameUrl, getTitleOnUiThread(mAwContents));
160
161         } finally {
162             if (webServer != null) webServer.shutdown();
163         }
164     }
165
166     @SmallTest
167     @Feature({"AndroidWebView"})
168     public void testCrossOrigin() throws Throwable {
169         TestWebServer webServer = null;
170         try {
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");
176
177             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
178             loadDataWithBaseUrlSync(html, "text/html", false, baseUrl, null);
179
180             assertEquals("Exception", getTitleOnUiThread(mAwContents));
181
182         } finally {
183             if (webServer != null) webServer.shutdown();
184         }
185     }
186
187     @SmallTest
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'>" +
192                 "</body></html>";
193         loadDataWithBaseUrlSync(pageHtml, "text/html", false, null, null);
194         assertEquals("about:blank", getTitleOnUiThread(mAwContents));
195     }
196
197     @SmallTest
198     @Feature({"AndroidWebView"})
199     public void testHistoryUrl() throws Throwable {
200
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));
209
210         loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, null);
211         assertEquals("about:blank", HistoryUtils.getUrlOnUiThread(
212                 getInstrumentation(), mContentViewCore));
213     }
214
215     @SmallTest
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));
223     }
224
225     /*
226     @SmallTest
227     @Feature({"AndroidWebView"})
228     http://crbug.com/173274
229     */
230     @DisabledTest
231     public void testHistoryUrlNavigation() throws Throwable {
232         TestWebServer webServer = null;
233         try {
234             webServer = new TestWebServer(false);
235             final String historyUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
236                     CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
237
238             final String page1Title = "Page1";
239             final String page1Html = "<html><head><title>" + page1Title + "</title>" +
240                     "<body>" + page1Title + "</body></html>";
241
242             loadDataWithBaseUrlSync(page1Html, "text/html", false, null, historyUrl);
243             assertEquals(page1Title, getTitleOnUiThread(mAwContents));
244
245             final String page2Title = "Page2";
246             final String page2Html = "<html><head><title>" + page2Title + "</title>" +
247                     "<body>" + page2Title + "</body></html>";
248
249             final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
250                     mContentsClient.getOnPageFinishedHelper();
251             loadDataSync(mAwContents, onPageFinishedHelper, page2Html, "text/html", false);
252             assertEquals(page2Title, getTitleOnUiThread(mAwContents));
253
254             HistoryUtils.goBackSync(getInstrumentation(), mContentViewCore, onPageFinishedHelper);
255             // The title of the 'about.html' specified via historyUrl.
256             assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
257
258         } finally {
259             if (webServer != null) webServer.shutdown();
260         }
261     }
262
263     /**
264      * @return true if |fileUrl| was accessible from a data url with |baseUrl| as it's
265      * base URL.
266      */
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 + "\';\" />" +
274                 "</body></html>";
275
276         loadDataWithBaseUrlSync(data, "text/html", false, baseUrl, null);
277
278         CriteriaHelper.pollForCriteria(new Criteria() {
279             @Override
280             public boolean isSatisfied() {
281                 try {
282                     String title = getTitleOnUiThread(mAwContents);
283                     return IMAGE_LOADED.equals(title) || IMAGE_NOT_LOADED.equals(title);
284                 } catch (Throwable t) {
285                     return false;
286                 }
287             }
288         });
289
290         return IMAGE_LOADED.equals(getTitleOnUiThread(mAwContents));
291     }
292
293     @SmallTest
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);
302         fos.close();
303         String imagePath = tempImage.getAbsolutePath();
304
305         AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
306         contentSettings.setImagesEnabled(true);
307         contentSettings.setJavaScriptEnabled(true);
308
309         try {
310             final String DATA_BASE_URL = "data:";
311             final String NON_DATA_BASE_URL = "http://example.com";
312
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));
322
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));
332
333             token += "a";
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));
343
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));
350         } finally {
351           if (!tempImage.delete()) throw new AssertionError();
352         }
353     }
354 }