Home | History | Annotate | Download | only in test
      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
     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 
     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 
     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 
     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"})
     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"})
    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"})
    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"})
    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"})
    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"})
    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"})
    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"})
    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"})
    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
    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      */
    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"})
    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