Cts tests for webview file origin policy changes

Bug: 6212665

Add cts test cases for testing file origin policy. Also fix a cts
test that can possible cause the test to freeze due to use of an
indefinite timeout.

Change-Id: I66d10747b5e677fd1e96e9399116a5b0dde7a20c
diff --git a/tests/assets/webkit/test_iframeaccess.html b/tests/assets/webkit/test_iframeaccess.html
new file mode 100644
index 0000000..1b5fc5a
--- /dev/null
+++ b/tests/assets/webkit/test_iframeaccess.html
@@ -0,0 +1,28 @@
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<html>
+    <head>
+        <title>DEFAULT TITLE</title>
+        <script type="text/javascript">
+          function load() {
+            document.title = document.getElementById('frame').contentWindow.location.href;
+          }
+         </script>
+    </head>
+    <body onload="load()">
+      <iframe id="frame" src="./test_hello_world.html"></iframe>
+    </body>
+</html>
diff --git a/tests/assets/webkit/test_imageaccess.html b/tests/assets/webkit/test_imageaccess.html
new file mode 100644
index 0000000..ce76c83
--- /dev/null
+++ b/tests/assets/webkit/test_imageaccess.html
@@ -0,0 +1,28 @@
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<html>
+    <head>
+        <title>TEST FAILED</title>
+    </head>
+    <body>
+        <img id='img' src="../images/tomato.png" onload="success()" />
+        <script>
+            function success() {
+              document.title=document.getElementById('img').naturalHeight;
+            }
+        </script>
+    </body>
+</html>
diff --git a/tests/tests/webkit/src/android/webkit/cts/ChromeClient.java b/tests/tests/webkit/src/android/webkit/cts/ChromeClient.java
new file mode 100644
index 0000000..3df78a4
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/ChromeClient.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit.cts;
+
+import android.webkit.ConsoleMessage;
+import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
+
+// A chrome client for listening webview chrome events.
+class ChromeClient extends WaitForProgressClient {
+    private boolean mIsMessageLevelAvailable;
+    private ConsoleMessage.MessageLevel mMessageLevel;
+
+    public ChromeClient(WebViewOnUiThread onUiThread) {
+        super(onUiThread);
+    }
+
+    @Override
+    public synchronized boolean onConsoleMessage(ConsoleMessage message) {
+        mMessageLevel = message.messageLevel();
+        mIsMessageLevelAvailable = true;
+        notify();
+        return true;
+    }
+
+    public synchronized ConsoleMessage.MessageLevel getMessageLevel(int timeout) {
+        for(; timeout > 0; timeout -= 1000) {
+            if( mIsMessageLevelAvailable ) break;
+            try {
+                wait(1000);
+            } catch (InterruptedException e) {
+            }
+        }
+        return mMessageLevel;
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index 18fdcab..63354d4 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -60,6 +60,8 @@
 
     public static final String PARAM_ASSET_URL = "webkit/test_queryparam.html";
     public static final String ANCHOR_ASSET_URL = "webkit/test_anchor.html";
+    public static final String IMAGE_ACCESS_URL = "webkit/test_imageaccess.html";
+    public static final String IFRAME_ACCESS_URL = "webkit/test_iframeaccess.html";
 
     // Must match the title of the page at
     // android/frameworks/base/core/res/res/raw/loaderror.html
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 27fa5a0..b969dd9 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -15,18 +15,19 @@
  */
 package android.webkit.cts;
 
+import android.content.Context;
 import android.cts.util.PollingCheck;
 import android.os.Build;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
+import android.webkit.ConsoleMessage;
 import android.webkit.MimeTypeMap;
-import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
 import android.webkit.WebSettings.LayoutAlgorithm;
 import android.webkit.WebSettings.RenderPriority;
 import android.webkit.WebSettings.TextSize;
 
-
+import java.io.FileOutputStream;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -54,6 +55,7 @@
     private WebSettings mSettings;
     private CtsTestServer mWebServer;
     private WebViewOnUiThread mOnUiThread;
+    private Context mContext;
 
     public WebSettingsTest() {
         super("com.android.cts.stub", WebViewStubActivity.class);
@@ -64,6 +66,7 @@
         super.setUp();
         mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
         mSettings = mOnUiThread.getSettings();
+        mContext = getInstrumentation().getTargetContext();
     }
 
     @Override
@@ -676,6 +679,106 @@
         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
     }
 
+    // Verify that an image in local file system can be loaded by an asset
+    public void testLocalImageLoads() throws Throwable {
+
+        mSettings.setJavaScriptEnabled(true);
+        // Check that local images are loaded without issues regardless of domain checkings
+        mSettings.setAllowUniversalAccessFromFileURLs(false);
+        mSettings.setAllowFileAccessFromFileURLs(false);
+        String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IMAGE_ACCESS_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        waitForNonEmptyImage();
+        assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
+    }
+
+    // Verify that javascript cross-domain request permissions matches file domain settings
+    // for iframes
+    public void testIframesWhenAccessFromFileURLsEnabled() throws Throwable {
+
+        mSettings.setJavaScriptEnabled(true);
+        // disable universal access from files
+        mSettings.setAllowUniversalAccessFromFileURLs(false);
+        mSettings.setAllowFileAccessFromFileURLs(true);
+
+        // when cross file scripting is enabled, make sure cross domain requests succeed
+        String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IFRAME_ACCESS_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        String iframeUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        assertEquals(iframeUrl, mOnUiThread.getTitle());
+    }
+
+    // Verify that javascript cross-domain request permissions matches file domain settings
+    // for iframes
+    public void testIframesWhenAccessFromFileURLsDisabled() throws Throwable {
+
+        mSettings.setJavaScriptEnabled(true);
+        // disable universal access from files
+        mSettings.setAllowUniversalAccessFromFileURLs(false);
+        mSettings.setAllowFileAccessFromFileURLs(false);
+
+        // when cross file scripting is disabled, make sure cross domain requests fail
+        final ChromeClient webChromeClient = new ChromeClient(mOnUiThread);
+        mOnUiThread.setWebChromeClient(webChromeClient);
+        String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IFRAME_ACCESS_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        String iframeUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        assertFalse(iframeUrl.equals(mOnUiThread.getTitle()));
+        assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel(10000));
+    }
+
+    // Verify that enabling file access from file URLs enable XmlHttpRequest (XHR) across files
+    public void testXHRWhenAccessFromFileURLsEnabled() throws Throwable {
+        verifyFileXHR(true);
+    }
+
+    // Verify that disabling file access from file URLs disable XmlHttpRequest (XHR) accross files
+    public void testXHRWhenAccessFromFileURLsDisabled() throws Throwable {
+
+        final ChromeClient webChromeClient = new ChromeClient(mOnUiThread);
+        mOnUiThread.setWebChromeClient(webChromeClient);
+        verifyFileXHR(false);
+        assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel(10000));
+    }
+
+    // verify XHR across files matches the allowFileAccessFromFileURLs setting
+    private void verifyFileXHR(boolean enableXHR) throws Throwable {
+        // target file content
+        String target ="<html><body>target</body><html>";
+
+        String targetPath = mContext.getFileStreamPath("target.html").getAbsolutePath();
+        // local file content that use XHR to read the target file
+        String local ="" +
+            "<html><body><script>" +
+            "var client = new XMLHttpRequest();" +
+            "client.open('GET', 'file://" + targetPath + "',false);" +
+            "client.send();" +
+            "document.title = client.responseText;" +
+            "</script></body></html>";
+
+        // create files in internal storage
+        writeFile("local.html", local);
+        writeFile("target.html", target);
+
+        mSettings.setJavaScriptEnabled(true);
+        // disable universal access from files
+        mSettings.setAllowUniversalAccessFromFileURLs(false);
+        mSettings.setAllowFileAccessFromFileURLs(enableXHR);
+        String localPath = mContext.getFileStreamPath("local.html").getAbsolutePath();
+        // when cross file scripting is enabled, make sure cross domain requests succeed
+        mOnUiThread.loadUrlAndWaitForCompletion("file://" + localPath);
+        if (enableXHR) assertEquals(target, mOnUiThread.getTitle());
+        else assertFalse(target.equals(mOnUiThread.getTitle()));
+    }
+
+    // Create a private file on internal storage from the given string
+    private void writeFile(String filename, String content) throws Exception {
+
+        FileOutputStream fos = mContext.openFileOutput(filename, Context.MODE_PRIVATE);
+        fos.write(content.getBytes());
+        fos.close();
+    }
+
     /**
      * Starts the internal web server. The server will be shut down automatically
      * during tearDown().
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 3477253..87d0dc3 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -745,34 +745,8 @@
                 "text/html", null);
         assertEquals("Hello,World!", mOnUiThread.getTitle());
 
-        // Test that JavaScript can't access cross-origin content.
-        class ConsoleMessageWebChromeClient extends WaitForProgressClient {
-            private boolean mIsMessageLevelAvailable;
-            private ConsoleMessage.MessageLevel mMessageLevel;
-
-            public ConsoleMessageWebChromeClient() {
-                super(mOnUiThread);
-            }
-
-            @Override
-            public synchronized boolean onConsoleMessage(ConsoleMessage message) {
-                mMessageLevel = message.messageLevel();
-                mIsMessageLevelAvailable = true;
-                notify();
-                return true;
-            }
-            public synchronized ConsoleMessage.MessageLevel getMessageLevel() {
-                while (!mIsMessageLevelAvailable) {
-                    try {
-                        wait();
-                    } catch (InterruptedException e) {
-                    }
-                }
-                return mMessageLevel;
-            }
-        }
         startWebServer(false);
-        final ConsoleMessageWebChromeClient webChromeClient = new ConsoleMessageWebChromeClient();
+        final ChromeClient webChromeClient = new ChromeClient(mOnUiThread);
         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         runTestOnUiThread(new Runnable() {
             @Override
@@ -787,7 +761,7 @@
                         "text/html", null);
             }
         });
-        assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel());
+        assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel(10000));
     }
 
     @UiThreadTest