| 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..27cf1d6181531b96f3fe52e87a6f11e41bcc4221 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 {
|
| @@ -566,10 +572,11 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase {
|
| @Feature({"AndroidWebView", "Android-JavaBridge"})
|
| public void testReflectPublicMethod() throws Throwable {
|
| injectObjectAndReload(new Object() {
|
| + public Class<?> myGetClass() { return getClass(); }
|
| public String method() { return "foo"; }
|
| }, "testObject");
|
| assertEquals("foo", executeJavaScriptAndGetStringResult(
|
| - "testObject.getClass().getMethod('method', null).invoke(testObject, null)" +
|
| + "testObject.myGetClass().getMethod('method', null).invoke(testObject, null)" +
|
| ".toString()"));
|
| }
|
|
|
| @@ -577,36 +584,40 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase {
|
| @Feature({"AndroidWebView", "Android-JavaBridge"})
|
| public void testReflectPublicField() throws Throwable {
|
| injectObjectAndReload(new Object() {
|
| + public Class<?> myGetClass() { return getClass(); }
|
| public String field = "foo";
|
| }, "testObject");
|
| assertEquals("foo", executeJavaScriptAndGetStringResult(
|
| - "testObject.getClass().getField('field').get(testObject).toString()"));
|
| + "testObject.myGetClass().getField('field').get(testObject).toString()"));
|
| }
|
|
|
| @SmallTest
|
| @Feature({"AndroidWebView", "Android-JavaBridge"})
|
| public void testReflectPrivateMethodRaisesException() throws Throwable {
|
| injectObjectAndReload(new Object() {
|
| + public Class<?> myGetClass() { return getClass(); }
|
| private void method() {};
|
| }, "testObject");
|
| - assertRaisesException("testObject.getClass().getMethod('method', null)");
|
| + assertRaisesException("testObject.myGetClass().getMethod('method', null)");
|
| // getDeclaredMethod() is able to access a private method, but invoke()
|
| // throws a Java exception.
|
| assertRaisesException(
|
| - "testObject.getClass().getDeclaredMethod('method', null).invoke(testObject, null)");
|
| + "testObject.myGetClass().getDeclaredMethod('method', null)." +
|
| + "invoke(testObject, null)");
|
| }
|
|
|
| @SmallTest
|
| @Feature({"AndroidWebView", "Android-JavaBridge"})
|
| public void testReflectPrivateFieldRaisesException() throws Throwable {
|
| injectObjectAndReload(new Object() {
|
| + public Class<?> myGetClass() { return getClass(); }
|
| private int field;
|
| }, "testObject");
|
| - assertRaisesException("testObject.getClass().getField('field')");
|
| + assertRaisesException("testObject.myGetClass().getField('field')");
|
| // getDeclaredField() is able to access a private field, but getInt()
|
| // throws a Java exception.
|
| assertRaisesException(
|
| - "testObject.getClass().getDeclaredField('field').getInt(testObject)");
|
| + "testObject.myGetClass().getDeclaredField('field').getInt(testObject)");
|
| }
|
|
|
| @SmallTest
|
| @@ -619,8 +630,8 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase {
|
| // Test calling a method of an explicitly inherited class (Base#allowed()).
|
| assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.allowed()"));
|
|
|
| - // Test calling a method of an implicitly inherited class (Object#getClass()).
|
| - assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject.getClass()"));
|
| + // Test calling a method of an implicitly inherited class (Object#toString()).
|
| + assertEquals("string", executeJavaScriptAndGetStringResult("typeof testObject.toString()"));
|
| }
|
|
|
| @SmallTest
|
| @@ -835,4 +846,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();
|
| + }
|
| }
|
|
|