• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics.Bitmap;
8 import android.test.suitebuilder.annotation.SmallTest;
9 
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;
19 
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.util.concurrent.Callable;
23 
24 /**
25  * Tests for the {@link android.webkit.WebView#loadDataWithBaseURL(String, String, String, String,
26  * String)} method.
27  */
28 public class LoadDataWithBaseUrlTest extends AwTestBase {
29 
30     private TestAwContentsClient mContentsClient;
31     private AwContents mAwContents;
32     private WebContents mWebContents;
33 
34     @Override
setUp()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         mWebContents = mAwContents.getWebContents();
42     }
43 
loadDataWithBaseUrlSync( final String data, final String mimeType, final boolean isBase64Encoded, final String baseUrl, final String historyUrl)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);
49     }
50 
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;";
55 
getScriptFileTestPageHtml(final String scriptUrl)56     private String getScriptFileTestPageHtml(final String scriptUrl) {
57         return "<html>" +
58                 "  <head>" +
59                 "    <title>" + SCRIPT_NOT_LOADED + "</title>" +
60                 "    <script src='" + scriptUrl + "'></script>" +
61                 "  </head>" +
62                 "  <body onload=\"if(script_was_loaded) document.title='" + SCRIPT_LOADED + "'\">" +
63                 "  </body>" +
64                 "</html>";
65     }
66 
getCrossOriginAccessTestPageHtml(final String iframeUrl)67     private String getCrossOriginAccessTestPageHtml(final String iframeUrl) {
68         return "<html>" +
69                 "  <head>" +
70                 "    <script>" +
71                 "      function onload() {" +
72                 "        try {" +
73                 "          document.title = " +
74                 "            document.getElementById('frame').contentWindow.location.href;" +
75                 "        } catch (e) {" +
76                 "          document.title = 'Exception';" +
77                 "        }" +
78                 "      }" +
79                 "    </script>" +
80                 "  </head>" +
81                 "  <body onload='onload()'>" +
82                 "    <iframe id='frame' src='" + iframeUrl + "'></iframe>" +
83                 "  </body>" +
84                 "</html>";
85     }
86 
87 
88     @SmallTest
89     @Feature({"AndroidWebView"})
testImageLoad()90     public void testImageLoad() throws Throwable {
91         TestWebServer webServer = null;
92         try {
93             webServer = new TestWebServer(false);
94             webServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME,
95                     CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
96 
97             AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
98             contentSettings.setImagesEnabled(true);
99             contentSettings.setJavaScriptEnabled(true);
100 
101             loadDataWithBaseUrlSync(
102                     CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
103                     "text/html", false, webServer.getBaseUrl(), null);
104 
105             assertEquals("5", getTitleOnUiThread(mAwContents));
106         } finally {
107             if (webServer != null) webServer.shutdown();
108         }
109     }
110 
111     @SmallTest
112     @Feature({"AndroidWebView"})
testScriptLoad()113     public void testScriptLoad() throws Throwable {
114         TestWebServer webServer = null;
115         try {
116             webServer = new TestWebServer(false);
117 
118             final String scriptUrl = webServer.setResponse(SCRIPT_FILE, SCRIPT_JS,
119                     CommonResources.getTextJavascriptHeaders(true));
120             final String pageHtml = getScriptFileTestPageHtml(scriptUrl);
121 
122             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
123             loadDataWithBaseUrlSync(pageHtml, "text/html", false, webServer.getBaseUrl(), null);
124             assertEquals(SCRIPT_LOADED, getTitleOnUiThread(mAwContents));
125 
126         } finally {
127             if (webServer != null) webServer.shutdown();
128         }
129     }
130 
131     @SmallTest
132     @Feature({"AndroidWebView"})
testSameOrigin()133     public void testSameOrigin() throws Throwable {
134         TestWebServer webServer = null;
135         try {
136             webServer = new TestWebServer(false);
137             final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
138                     CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
139             final String html = getCrossOriginAccessTestPageHtml(frameUrl);
140 
141             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
142             loadDataWithBaseUrlSync(html, "text/html", false, webServer.getBaseUrl(), null);
143             assertEquals(frameUrl, getTitleOnUiThread(mAwContents));
144 
145         } finally {
146             if (webServer != null) webServer.shutdown();
147         }
148     }
149 
150     @SmallTest
151     @Feature({"AndroidWebView"})
testCrossOrigin()152     public void testCrossOrigin() throws Throwable {
153         TestWebServer webServer = null;
154         try {
155             webServer = new TestWebServer(false);
156             final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
157                     CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
158             final String html = getCrossOriginAccessTestPageHtml(frameUrl);
159             final String baseUrl = webServer.getBaseUrl().replaceFirst("localhost", "127.0.0.1");
160 
161             getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
162             loadDataWithBaseUrlSync(html, "text/html", false, baseUrl, null);
163 
164             assertEquals("Exception", getTitleOnUiThread(mAwContents));
165 
166         } finally {
167             if (webServer != null) webServer.shutdown();
168         }
169     }
170 
171     @SmallTest
172     @Feature({"AndroidWebView"})
testNullBaseUrl()173     public void testNullBaseUrl() throws Throwable {
174         getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
175         final String pageHtml = "<html><body onload='document.title=document.location.href'>" +
176                 "</body></html>";
177         loadDataWithBaseUrlSync(pageHtml, "text/html", false, null, null);
178         assertEquals("about:blank", getTitleOnUiThread(mAwContents));
179     }
180 
181     @SmallTest
182     @Feature({"AndroidWebView"})
testloadDataWithBaseUrlCallsOnPageStarted()183     public void testloadDataWithBaseUrlCallsOnPageStarted() throws Throwable {
184         final String baseUrl = "http://base.com/";
185         TestCallbackHelperContainer.OnPageStartedHelper onPageStartedHelper =
186                 mContentsClient.getOnPageStartedHelper();
187         final int callCount = onPageStartedHelper.getCallCount();
188         loadDataWithBaseUrlAsync(mAwContents, CommonResources.ABOUT_HTML, "text/html", false,
189                 baseUrl, "about:blank");
190         onPageStartedHelper.waitForCallback(callCount);
191         assertEquals(baseUrl, onPageStartedHelper.getUrl());
192     }
193 
194     @SmallTest
195     @Feature({"AndroidWebView"})
testHistoryUrl()196     public void testHistoryUrl() throws Throwable {
197 
198         final String pageHtml = "<html><body>Hello, world!</body></html>";
199         final String baseUrl = "http://example.com";
200         // TODO(mnaganov): Use the same string as Android CTS suite uses
201         // once GURL issue is resolved (http://code.google.com/p/google-url/issues/detail?id=29)
202         final String historyUrl = "http://history.com/";
203         loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, historyUrl);
204         assertEquals(historyUrl, HistoryUtils.getUrlOnUiThread(
205                 getInstrumentation(), mWebContents));
206 
207         loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, null);
208         assertEquals("about:blank", HistoryUtils.getUrlOnUiThread(
209                 getInstrumentation(), mWebContents));
210     }
211 
212     @SmallTest
213     @Feature({"AndroidWebView"})
testOnPageFinishedUrlIsBaseUrl()214     public void testOnPageFinishedUrlIsBaseUrl() throws Throwable {
215         final String pageHtml = "<html><body>Hello, world!</body></html>";
216         final String baseUrl = "http://example.com/";
217         loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
218         loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
219         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
220                 mContentsClient.getOnPageFinishedHelper();
221         assertEquals(baseUrl, onPageFinishedHelper.getUrl());
222     }
223 
224     @SmallTest
225     @Feature({"AndroidWebView"})
testHistoryUrlIgnoredWithDataSchemeBaseUrl()226     public void testHistoryUrlIgnoredWithDataSchemeBaseUrl() throws Throwable {
227         final String pageHtml = "<html><body>bar</body></html>";
228         final String historyUrl = "http://history.com/";
229         loadDataWithBaseUrlSync(pageHtml, "text/html", false, "data:foo", historyUrl);
230         assertEquals("data:text/html," + pageHtml, HistoryUtils.getUrlOnUiThread(
231                 getInstrumentation(), mWebContents));
232     }
233 
234     /*
235     @SmallTest
236     @Feature({"AndroidWebView"})
237     http://crbug.com/173274
238     */
239     @DisabledTest
testHistoryUrlNavigation()240     public void testHistoryUrlNavigation() throws Throwable {
241         TestWebServer webServer = null;
242         try {
243             webServer = new TestWebServer(false);
244             final String historyUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
245                     CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
246 
247             final String page1Title = "Page1";
248             final String page1Html = "<html><head><title>" + page1Title + "</title>" +
249                     "<body>" + page1Title + "</body></html>";
250 
251             loadDataWithBaseUrlSync(page1Html, "text/html", false, null, historyUrl);
252             assertEquals(page1Title, getTitleOnUiThread(mAwContents));
253 
254             final String page2Title = "Page2";
255             final String page2Html = "<html><head><title>" + page2Title + "</title>" +
256                     "<body>" + page2Title + "</body></html>";
257 
258             final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
259                     mContentsClient.getOnPageFinishedHelper();
260             loadDataSync(mAwContents, onPageFinishedHelper, page2Html, "text/html", false);
261             assertEquals(page2Title, getTitleOnUiThread(mAwContents));
262 
263             HistoryUtils.goBackSync(getInstrumentation(), mWebContents, onPageFinishedHelper);
264             // The title of the 'about.html' specified via historyUrl.
265             assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
266 
267         } finally {
268             if (webServer != null) webServer.shutdown();
269         }
270     }
271 
272     /**
273      * @return true if |fileUrl| was accessible from a data url with |baseUrl| as it's
274      * base URL.
275      */
canAccessFileFromData(String baseUrl, String fileUrl)276     private boolean canAccessFileFromData(String baseUrl, String fileUrl) throws Throwable {
277         final String imageLoaded = "LOADED";
278         final String imageNotLoaded = "NOT_LOADED";
279         String data = "<html><body>" +
280                 "<img src=\"" + fileUrl + "\" " +
281                 "onload=\"document.title=\'" + imageLoaded + "\';\" " +
282                 "onerror=\"document.title=\'" + imageNotLoaded + "\';\" />" +
283                 "</body></html>";
284 
285         loadDataWithBaseUrlSync(data, "text/html", false, baseUrl, null);
286 
287         poll(new Callable<Boolean>() {
288             @Override
289             public Boolean call() throws Exception {
290                 String title = getTitleOnUiThread(mAwContents);
291                 return imageLoaded.equals(title) || imageNotLoaded.equals(title);
292             }
293         });
294 
295         return imageLoaded.equals(getTitleOnUiThread(mAwContents));
296     }
297 
298     @SmallTest
299     @Feature({"AndroidWebView"})
testLoadDataWithBaseUrlAccessingFile()300     public void testLoadDataWithBaseUrlAccessingFile() throws Throwable {
301         // Create a temporary file on the filesystem we can try to read.
302         File cacheDir = getActivity().getCacheDir();
303         File tempImage = File.createTempFile("test_image", ".png", cacheDir);
304         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
305         FileOutputStream fos = new FileOutputStream(tempImage);
306         bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
307         fos.close();
308         String imagePath = tempImage.getAbsolutePath();
309 
310         AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
311         contentSettings.setImagesEnabled(true);
312         contentSettings.setJavaScriptEnabled(true);
313 
314         try {
315             final String dataBaseUrl = "data:";
316             final String nonDataBaseUrl = "http://example.com";
317 
318             mAwContents.getSettings().setAllowFileAccess(false);
319             String token = "" + System.currentTimeMillis();
320             // All access to file://, including android_asset and android_res is blocked
321             // with a data: base URL, regardless of AwSettings.getAllowFileAccess().
322             assertFalse(canAccessFileFromData(dataBaseUrl,
323                   "file:///android_asset/asset_icon.png?" + token));
324             assertFalse(canAccessFileFromData(dataBaseUrl,
325                   "file:///android_res/raw/resource_icon.png?" + token));
326             assertFalse(canAccessFileFromData(dataBaseUrl, "file://" + imagePath + "?" + token));
327 
328             // WebView always has access to android_asset and android_res for non-data
329             // base URLs and can access other file:// URLs based on the value of
330             // AwSettings.getAllowFileAccess().
331             assertTrue(canAccessFileFromData(nonDataBaseUrl,
332                   "file:///android_asset/asset_icon.png?" + token));
333             assertTrue(canAccessFileFromData(nonDataBaseUrl,
334                   "file:///android_res/raw/resource_icon.png?" + token));
335             assertFalse(canAccessFileFromData(nonDataBaseUrl,
336                   "file://" + imagePath + "?" + token));
337 
338             token += "a";
339             mAwContents.getSettings().setAllowFileAccess(true);
340             // We should still be unable to access any file:// with when loading with a
341             // data: base URL, but we should now be able to access the wider file system
342             // (still restricted by OS-level permission checks) with a non-data base URL.
343             assertFalse(canAccessFileFromData(dataBaseUrl,
344                   "file:///android_asset/asset_icon.png?" + token));
345             assertFalse(canAccessFileFromData(dataBaseUrl,
346                   "file:///android_res/raw/resource_icon.png?" + token));
347             assertFalse(canAccessFileFromData(dataBaseUrl, "file://" + imagePath + "?" + token));
348 
349             assertTrue(canAccessFileFromData(nonDataBaseUrl,
350                   "file:///android_asset/asset_icon.png?" + token));
351             assertTrue(canAccessFileFromData(nonDataBaseUrl,
352                   "file:///android_res/raw/resource_icon.png?" + token));
353             assertTrue(canAccessFileFromData(nonDataBaseUrl,
354                   "file://" + imagePath + "?" + token));
355         } finally {
356             if (!tempImage.delete()) throw new AssertionError();
357         }
358     }
359 }
360