root/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setUp
  2. loadDataWithBaseUrlSync
  3. getScriptFileTestPageHtml
  4. getCrossOriginAccessTestPageHtml
  5. Feature
  6. testImageLoad
  7. Feature
  8. testScriptLoad
  9. Feature
  10. testSameOrigin
  11. Feature
  12. testCrossOrigin
  13. Feature
  14. testNullBaseUrl
  15. Feature
  16. testloadDataWithBaseUrlCallsOnPageStarted
  17. Feature
  18. testHistoryUrl
  19. Feature
  20. testOnPageFinishedUrlIsBaseUrl
  21. Feature
  22. testHistoryUrlIgnoredWithDataSchemeBaseUrl
  23. testHistoryUrlNavigation
  24. canAccessFileFromData
  25. Feature
  26. testLoadDataWithBaseUrlAccessingFile

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.android_webview.test;

import android.graphics.Bitmap;
import android.test.suitebuilder.annotation.SmallTest;

import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.test.util.CommonResources;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content.browser.test.util.HistoryUtils;
import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
import org.chromium.net.test.util.TestWebServer;

import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.Callable;

/**
 * Tests for the {@link android.webkit.WebView#loadDataWithBaseURL(String, String, String, String,
 * String)} method.
 */
public class LoadDataWithBaseUrlTest extends AwTestBase {

    private TestAwContentsClient mContentsClient;
    private AwContents mAwContents;
    private ContentViewCore mContentViewCore;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mContentsClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(mContentsClient);
        mAwContents = testContainerView.getAwContents();
        mContentViewCore = testContainerView.getContentViewCore();
    }

    protected void loadDataWithBaseUrlSync(
        final String data, final String mimeType, final boolean isBase64Encoded,
        final String baseUrl, final String historyUrl) throws Throwable {
        loadDataWithBaseUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
                data, mimeType, isBase64Encoded, baseUrl, historyUrl);
    }

    private static final String SCRIPT_FILE = "/script.js";
    private static final String SCRIPT_LOADED = "Loaded";
    private static final String SCRIPT_NOT_LOADED = "Not loaded";
    private static final String SCRIPT_JS = "script_was_loaded = true;";

    private String getScriptFileTestPageHtml(final String scriptUrl) {
        return "<html>" +
                "  <head>" +
                "    <title>" + SCRIPT_NOT_LOADED + "</title>" +
                "    <script src='" + scriptUrl + "'></script>" +
                "  </head>" +
                "  <body onload=\"if(script_was_loaded) document.title='" + SCRIPT_LOADED + "'\">" +
                "  </body>" +
                "</html>";
    }

    private String getCrossOriginAccessTestPageHtml(final String iframeUrl) {
        return "<html>" +
                "  <head>" +
                "    <script>" +
                "      function onload() {" +
                "        try {" +
                "          document.title = " +
                "            document.getElementById('frame').contentWindow.location.href;" +
                "        } catch (e) {" +
                "          document.title = 'Exception';" +
                "        }" +
                "      }" +
                "    </script>" +
                "  </head>" +
                "  <body onload='onload()'>" +
                "    <iframe id='frame' src='" + iframeUrl + "'></iframe>" +
                "  </body>" +
                "</html>";
    }


    @SmallTest
    @Feature({"AndroidWebView"})
    public void testImageLoad() throws Throwable {
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            webServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME,
                    CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));

            AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
            contentSettings.setImagesEnabled(true);
            contentSettings.setJavaScriptEnabled(true);

            loadDataWithBaseUrlSync(
                    CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
                    "text/html", false, webServer.getBaseUrl(), null);

            assertEquals("5", getTitleOnUiThread(mAwContents));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testScriptLoad() throws Throwable {
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);

            final String scriptUrl = webServer.setResponse(SCRIPT_FILE, SCRIPT_JS,
                    CommonResources.getTextJavascriptHeaders(true));
            final String pageHtml = getScriptFileTestPageHtml(scriptUrl);

            getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
            loadDataWithBaseUrlSync(pageHtml, "text/html", false, webServer.getBaseUrl(), null);
            assertEquals(SCRIPT_LOADED, getTitleOnUiThread(mAwContents));

        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testSameOrigin() throws Throwable {
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
                    CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
            final String html = getCrossOriginAccessTestPageHtml(frameUrl);

            getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
            loadDataWithBaseUrlSync(html, "text/html", false, webServer.getBaseUrl(), null);
            assertEquals(frameUrl, getTitleOnUiThread(mAwContents));

        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testCrossOrigin() throws Throwable {
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String frameUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
                    CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
            final String html = getCrossOriginAccessTestPageHtml(frameUrl);
            final String baseUrl = webServer.getBaseUrl().replaceFirst("localhost", "127.0.0.1");

            getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
            loadDataWithBaseUrlSync(html, "text/html", false, baseUrl, null);

            assertEquals("Exception", getTitleOnUiThread(mAwContents));

        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testNullBaseUrl() throws Throwable {
        getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true);
        final String pageHtml = "<html><body onload='document.title=document.location.href'>" +
                "</body></html>";
        loadDataWithBaseUrlSync(pageHtml, "text/html", false, null, null);
        assertEquals("about:blank", getTitleOnUiThread(mAwContents));
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testloadDataWithBaseUrlCallsOnPageStarted() throws Throwable {
        final String baseUrl = "http://base.com/";
        TestCallbackHelperContainer.OnPageStartedHelper onPageStartedHelper =
                mContentsClient.getOnPageStartedHelper();
        final int callCount = onPageStartedHelper.getCallCount();
        loadDataWithBaseUrlAsync(mAwContents, CommonResources.ABOUT_HTML, "text/html", false,
                baseUrl, "about:blank");
        onPageStartedHelper.waitForCallback(callCount);
        assertEquals(baseUrl, onPageStartedHelper.getUrl());
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testHistoryUrl() throws Throwable {

        final String pageHtml = "<html><body>Hello, world!</body></html>";
        final String baseUrl = "http://example.com";
        // TODO(mnaganov): Use the same string as Android CTS suite uses
        // once GURL issue is resolved (http://code.google.com/p/google-url/issues/detail?id=29)
        final String historyUrl = "http://history.com/";
        loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, historyUrl);
        assertEquals(historyUrl, HistoryUtils.getUrlOnUiThread(
                getInstrumentation(), mContentViewCore));

        loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, null);
        assertEquals("about:blank", HistoryUtils.getUrlOnUiThread(
                getInstrumentation(), mContentViewCore));
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testOnPageFinishedUrlIsBaseUrl() throws Throwable {
        final String pageHtml = "<html><body>Hello, world!</body></html>";
        final String baseUrl = "http://example.com/";
        loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
        loadDataWithBaseUrlSync(pageHtml, "text/html", false, baseUrl, baseUrl);
        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                mContentsClient.getOnPageFinishedHelper();
        assertEquals(baseUrl, onPageFinishedHelper.getUrl());
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testHistoryUrlIgnoredWithDataSchemeBaseUrl() throws Throwable {
        final String pageHtml = "<html><body>bar</body></html>";
        final String historyUrl = "http://history.com/";
        loadDataWithBaseUrlSync(pageHtml, "text/html", false, "data:foo", historyUrl);
        assertEquals("data:text/html," + pageHtml, HistoryUtils.getUrlOnUiThread(
                getInstrumentation(), mContentViewCore));
    }

    /*
    @SmallTest
    @Feature({"AndroidWebView"})
    http://crbug.com/173274
    */
    @DisabledTest
    public void testHistoryUrlNavigation() throws Throwable {
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String historyUrl = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
                    CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));

            final String page1Title = "Page1";
            final String page1Html = "<html><head><title>" + page1Title + "</title>" +
                    "<body>" + page1Title + "</body></html>";

            loadDataWithBaseUrlSync(page1Html, "text/html", false, null, historyUrl);
            assertEquals(page1Title, getTitleOnUiThread(mAwContents));

            final String page2Title = "Page2";
            final String page2Html = "<html><head><title>" + page2Title + "</title>" +
                    "<body>" + page2Title + "</body></html>";

            final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                    mContentsClient.getOnPageFinishedHelper();
            loadDataSync(mAwContents, onPageFinishedHelper, page2Html, "text/html", false);
            assertEquals(page2Title, getTitleOnUiThread(mAwContents));

            HistoryUtils.goBackSync(getInstrumentation(), mContentViewCore, onPageFinishedHelper);
            // The title of the 'about.html' specified via historyUrl.
            assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));

        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    /**
     * @return true if |fileUrl| was accessible from a data url with |baseUrl| as it's
     * base URL.
     */
    private boolean canAccessFileFromData(String baseUrl, String fileUrl) throws Throwable {
        final String IMAGE_LOADED = "LOADED";
        final String IMAGE_NOT_LOADED = "NOT_LOADED";
        String data = "<html><body>" +
                "<img src=\"" + fileUrl + "\" " +
                "onload=\"document.title=\'" + IMAGE_LOADED + "\';\" " +
                "onerror=\"document.title=\'" + IMAGE_NOT_LOADED + "\';\" />" +
                "</body></html>";

        loadDataWithBaseUrlSync(data, "text/html", false, baseUrl, null);

        poll(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                String title = getTitleOnUiThread(mAwContents);
                return IMAGE_LOADED.equals(title) || IMAGE_NOT_LOADED.equals(title);
            }
        });

        return IMAGE_LOADED.equals(getTitleOnUiThread(mAwContents));
    }

    @SmallTest
    @Feature({"AndroidWebView"})
    public void testLoadDataWithBaseUrlAccessingFile() throws Throwable {
        // Create a temporary file on the filesystem we can try to read.
        File cacheDir = getActivity().getCacheDir();
        File tempImage = File.createTempFile("test_image", ".png", cacheDir);
        Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
        FileOutputStream fos = new FileOutputStream(tempImage);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.close();
        String imagePath = tempImage.getAbsolutePath();

        AwSettings contentSettings = getAwSettingsOnUiThread(mAwContents);
        contentSettings.setImagesEnabled(true);
        contentSettings.setJavaScriptEnabled(true);

        try {
            final String DATA_BASE_URL = "data:";
            final String NON_DATA_BASE_URL = "http://example.com";

            mAwContents.getSettings().setAllowFileAccess(false);
            String token = "" + System.currentTimeMillis();
            // All access to file://, including android_asset and android_res is blocked
            // with a data: base URL, regardless of AwSettings.getAllowFileAccess().
            assertFalse(canAccessFileFromData(DATA_BASE_URL,
                  "file:///android_asset/asset_icon.png?" + token));
            assertFalse(canAccessFileFromData(DATA_BASE_URL,
                  "file:///android_res/raw/resource_icon.png?" + token));
            assertFalse(canAccessFileFromData(DATA_BASE_URL, "file://" + imagePath + "?" + token));

            // WebView always has access to android_asset and android_res for non-data
            // base URLs and can access other file:// URLs based on the value of
            // AwSettings.getAllowFileAccess().
            assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file:///android_asset/asset_icon.png?" + token));
            assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file:///android_res/raw/resource_icon.png?" + token));
            assertFalse(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file://" + imagePath + "?" + token));

            token += "a";
            mAwContents.getSettings().setAllowFileAccess(true);
            // We should still be unable to access any file:// with when loading with a
            // data: base URL, but we should now be able to access the wider file system
            // (still restricted by OS-level permission checks) with a non-data base URL.
            assertFalse(canAccessFileFromData(DATA_BASE_URL,
                  "file:///android_asset/asset_icon.png?" + token));
            assertFalse(canAccessFileFromData(DATA_BASE_URL,
                  "file:///android_res/raw/resource_icon.png?" + token));
            assertFalse(canAccessFileFromData(DATA_BASE_URL, "file://" + imagePath + "?" + token));

            assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file:///android_asset/asset_icon.png?" + token));
            assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file:///android_res/raw/resource_icon.png?" + token));
            assertTrue(canAccessFileFromData(NON_DATA_BASE_URL,
                  "file://" + imagePath + "?" + token));
        } finally {
            if (!tempImage.delete()) throw new AssertionError();
        }
    }
}

/* [<][>][^][v][top][bottom][index][help] */