Index: content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java |
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java |
index 88c4b3b742f8aad08a5eb0be4bb3b82bc7d45b1c..316c8661d9e7029ebbb7d760655de58c3e3123bf 100644 |
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java |
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java |
@@ -4,6 +4,8 @@ |
package org.chromium.content.browser; |
+import android.os.Handler; |
+import android.os.Looper; |
import android.test.suitebuilder.annotation.SmallTest; |
import org.chromium.base.test.util.DisabledTest; |
@@ -70,6 +72,10 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { |
waitForResult(); |
return mBooleanValue; |
} |
+ |
+ public synchronized String getStringValue() { |
+ return mStringValue; |
+ } |
} |
private static class ObjectWithStaticMethod { |
@@ -835,4 +841,66 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { |
assertEquals("", executeJavaScriptAndGetStringResult( |
String.format(jsForInTestTemplate, nonInspectableObjectName))); |
} |
+ |
+ @SmallTest |
+ @Feature({"AndroidWebView", "Android-JavaBridge"}) |
+ public void testAccessToObjectGetClassIsBlocked() throws Throwable { |
+ injectObjectAndReload(new Object(), "testObject"); |
+ assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.getClass")); |
+ boolean securityExceptionThrown = false; |
+ try { |
+ final String result = executeJavaScriptAndWaitForExceptionSynchronously( |
+ "typeof testObject.getClass()"); |
+ fail("A call to java.lang.Object.getClass has been allowed, result: '" + result + "'"); |
+ } catch (SecurityException exception) { |
+ securityExceptionThrown = true; |
+ } |
+ assertTrue(securityExceptionThrown); |
+ } |
+ |
+ // Unlike executeJavaScriptAndGetStringResult, this method is sitting on the UI thread |
+ // until a non-null result is obtained or a Java exception has been thrown. This method is |
+ // capable of catching Java RuntimeExceptions happening on the UI thread asynchronously. |
+ private String executeJavaScriptAndWaitForExceptionSynchronously(final String script) |
+ throws Throwable { |
+ class ExitLoopException extends RuntimeException { |
+ } |
+ mTestController.setStringValue(null); |
+ runTestOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ getContentView().loadUrl(new LoadUrlParams("javascript:(function() { " + |
+ "testController.setStringValue(" + script + ") })()")); |
+ do { |
+ final Boolean[] deactivateExitLoopTask = new Boolean[1]; |
+ deactivateExitLoopTask[0] = false; |
+ // We can't use Loop.quit(), as this is the main looper, so we throw |
+ // an exception to bail out from the loop. |
+ new Handler(Looper.myLooper()).post(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (!deactivateExitLoopTask[0]) { |
+ throw new ExitLoopException(); |
+ } |
+ } |
+ }); |
+ try { |
+ Looper.loop(); |
+ } catch (ExitLoopException e) { |
+ // Intentionally empty. |
+ } catch (RuntimeException e) { |
+ // Prevent the task that throws the ExitLoopException from exploding |
+ // on the main loop outside of this function. |
+ deactivateExitLoopTask[0] = true; |
+ throw e; |
+ } |
+ } while (mTestController.getStringValue() == null || |
+ // When an exception in an injected method happens, the function returns |
+ // null. We ignore this and wait until the exception on the browser side |
+ // will be thrown. |
+ mTestController.getStringValue().equals("null")); |
+ } |
+ }); |
+ return mTestController.getStringValue(); |
+ } |
} |