// Note that this requires that we can pass a JavaScript boolean to Java.
private void assertRaisesException(String script) throws Throwable {
- executeJavaScript("try {" +
- script + ";" +
- " testController.setBooleanValue(false);" +
- "} catch (exception) {" +
- " testController.setBooleanValue(true);" +
- "}");
+ executeJavaScript("try {"
+ + script + ";"
+ + " testController.setBooleanValue(false);"
+ + "} catch (exception) {"
+ + " testController.setBooleanValue(true);"
+ + "}");
assertTrue(mTestController.waitForBooleanValue());
}
@SmallTest
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testRemovalNotReflectedUntilReload() throws Throwable {
- injectObjectAndReload(new Object(), "testObject");
+ injectObjectAndReload(new Object() {
+ public void method() {
+ mTestController.setStringValue("I'm here");
+ }
+ }, "testObject");
assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
+ executeJavaScript("testObject.method()");
+ assertEquals("I'm here", mTestController.waitForStringValue());
runTestOnUiThread(new Runnable() {
@Override
public void run() {
getContentViewCore().removeJavascriptInterface("testObject");
}
});
+ // Check that the Java object is being held by the Java bridge, thus it's not
+ // collected. Note that despite that what JavaDoc says about invoking "gc()", both Dalvik
+ // and ART actually run the collector if called via Runtime.
+ Runtime.getRuntime().gc();
assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
+ executeJavaScript("testObject.method()");
+ assertEquals("I'm here", mTestController.waitForStringValue());
synchronousPageReload();
assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
}
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testUncaughtJavaExceptionRaisesJavaScriptException() throws Throwable {
injectObjectAndReload(new Object() {
- public void method() { throw new RuntimeException("foo"); }
+ public void method() {
+ throw new RuntimeException("foo");
+ }
}, "testObject");
assertRaisesException("testObject.method()");
}
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testReplaceInjectedObject() throws Throwable {
injectObjectAndReload(new Object() {
- public void method() { mTestController.setStringValue("object 1"); }
+ public void method() {
+ mTestController.setStringValue("object 1");
+ }
}, "testObject");
executeJavaScript("testObject.method()");
assertEquals("object 1", mTestController.waitForStringValue());
injectObjectAndReload(new Object() {
- public void method() { mTestController.setStringValue("object 2"); }
+ public void method() {
+ mTestController.setStringValue("object 2");
+ }
}, "testObject");
executeJavaScript("testObject.method()");
assertEquals("object 2", mTestController.waitForStringValue());
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable {
injectObjectAndReload(new Object() {
- public void method() { mTestController.setStringValue("0 args"); }
- public void method(int x) { mTestController.setStringValue("1 arg"); }
- public void method(int x, int y) { mTestController.setStringValue("2 args"); }
+ public void method() {
+ mTestController.setStringValue("0 args");
+ }
+
+ public void method(int x) {
+ mTestController.setStringValue("1 arg");
+ }
+
+ public void method(int x, int y) {
+ mTestController.setStringValue("2 args");
+ }
}, "testObject");
executeJavaScript("testObject.method()");
assertEquals("0 args", mTestController.waitForStringValue());
public void testSameObjectInjectedMultipleTimes() throws Throwable {
class TestObject {
private int mNumMethodInvocations;
- public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
+
+ public void method() {
+ mTestController.setIntValue(++mNumMethodInvocations);
+ }
}
final TestObject testObject = new TestObject();
TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
injectObjectAndReload(new Object() {
public Object getInnerObject() {
return new Object() {
- public void method(int x) { mTestController.setIntValue(x); }
+ public void method(int x) {
+ mTestController.setIntValue(x);
+ }
};
}
}, "testObject");
public void testReturnedObjectInjectedElsewhere() throws Throwable {
class InnerObject {
private int mNumMethodInvocations;
- public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
+
+ public void method() {
+ mTestController.setIntValue(++mNumMethodInvocations);
+ }
}
final InnerObject innerObject = new InnerObject();
final Object object = new Object() {
// Initially, store a reference to the inner object in JS to make sure it's not
// garbage-collected prematurely.
assertEquals("object", executeJavaScriptAndGetStringResult(
- "(function() { " +
- "globalInner = testObject.getInnerObject(); return typeof globalInner; " +
- "})()"));
+ "(function() { "
+ + "globalInner = testObject.getInnerObject(); return typeof globalInner; "
+ + "})()"));
assertTrue(object.mWeakRefForInner.get() != null);
// Check that returned Java object is being held by the Java bridge, thus it's not
// collected. Note that despite that what JavaDoc says about invoking "gc()", both Dalvik
assertTrue(object.mWeakRefForInner.get() != null);
// Now dereference the inner object in JS and run GC to collect the interface object.
assertEquals("true", executeJavaScriptAndGetStringResult(
- "(function() { " +
- "delete globalInner; gc(); return (typeof globalInner == 'undefined'); " +
- "})()"));
+ "(function() { "
+ + "delete globalInner; gc(); return (typeof globalInner == 'undefined'); "
+ + "})()"));
// Force GC on the Java side again. The bridge had to release the inner object, so it must
// be collected this time.
Runtime.getRuntime().gc();
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testPublicInheritedMethod() throws Throwable {
class Base {
- public void method(int x) { mTestController.setIntValue(x); }
+ public void method(int x) {
+ mTestController.setIntValue(x);
+ }
}
class Derived extends Base {
}
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testOverriddenMethod() throws Throwable {
class Base {
- public void method() { mTestController.setStringValue("base"); }
+ public void method() {
+ mTestController.setStringValue("base");
+ }
}
class Derived extends Base {
@Override
- public void method() { mTestController.setStringValue("derived"); }
+ public void method() {
+ mTestController.setStringValue("derived");
+ }
}
injectObjectAndReload(new Derived(), "testObject");
executeJavaScript("testObject.method()");
private int mPrivateField;
}, "testObject");
executeJavaScript(
- "var result = \"\"; " +
- "for (x in testObject) { result += \" \" + x } " +
- "testController.setStringValue(result);");
+ "var result = \"\"; "
+ + "for (x in testObject) { result += \" \" + x } "
+ + "testController.setStringValue(result);");
assertEquals(" equals getClass hashCode method notify notifyAll toString wait",
mTestController.waitForStringValue());
}
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testReflectPublicMethod() throws Throwable {
injectObjectAndReload(new Object() {
- public Class<?> myGetClass() { return getClass(); }
- public String method() { return "foo"; }
+ public Class<?> myGetClass() {
+ return getClass();
+ }
+
+ public String method() {
+ return "foo";
+ }
}, "testObject");
assertEquals("foo", executeJavaScriptAndGetStringResult(
- "testObject.myGetClass().getMethod('method', null).invoke(testObject, null)" +
- ".toString()"));
+ "testObject.myGetClass().getMethod('method', null).invoke(testObject, null)"
+ + ".toString()"));
}
@SmallTest
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testReflectPublicField() throws Throwable {
injectObjectAndReload(new Object() {
- public Class<?> myGetClass() { return getClass(); }
+ public Class<?> myGetClass() {
+ return getClass();
+ }
+
public String field = "foo";
}, "testObject");
assertEquals("foo", executeJavaScriptAndGetStringResult(
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testReflectPrivateMethodRaisesException() throws Throwable {
injectObjectAndReload(new Object() {
- public Class<?> myGetClass() { return getClass(); }
+ public Class<?> myGetClass() {
+ return getClass();
+ }
+
private void method() {};
}, "testObject");
assertRaisesException("testObject.myGetClass().getMethod('method', null)");
// getDeclaredMethod() is able to access a private method, but invoke()
// throws a Java exception.
assertRaisesException(
- "testObject.myGetClass().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(); }
+ public Class<?> myGetClass() {
+ return getClass();
+ }
+
private int mField;
}, "testObject");
assertRaisesException("testObject.myGetClass().getField('field')");
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testAllowNonAnnotatedMethods() throws Throwable {
injectObjectAndReload(new Object() {
- public String allowed() { return "foo"; }
+ public String allowed() {
+ return "foo";
+ }
}, "testObject", null);
// Test calling a method of an explicitly inherited class (Base#allowed()).
public void testAllowOnlyAnnotatedMethods() throws Throwable {
injectObjectAndReload(new Object() {
@JavascriptInterface
- public String allowed() { return "foo"; }
+ public String allowed() {
+ return "foo";
+ }
- public String disallowed() { return "bar"; }
+ public String disallowed() {
+ return "bar";
+ }
}, "testObject", JavascriptInterface.class);
// getClass() is an Object method and does not have the @JavascriptInterface annotation and
public void testAnnotationRequirementRetainsPropertyAcrossObjects() throws Throwable {
class Test {
@JavascriptInterface
- public String safe() { return "foo"; }
+ public String safe() {
+ return "foo";
+ }
- public String unsafe() { return "bar"; }
+ public String unsafe() {
+ return "bar";
+ }
}
class TestReturner {
@JavascriptInterface
- public Test getTest() { return new Test(); }
+ public Test getTest() {
+ return new Test();
+ }
}
// First test with safe mode off.
public void testCustomAnnotationRestriction() throws Throwable {
class Test {
@TestAnnotation
- public String checkTestAnnotationFoo() { return "bar"; }
+ public String checkTestAnnotationFoo() {
+ return "bar";
+ }
@JavascriptInterface
- public String checkJavascriptInterfaceFoo() { return "bar"; }
+ public String checkJavascriptInterfaceFoo() {
+ return "bar";
+ }
}
// Inject javascriptInterfaceObj and require the JavascriptInterface annotation.
@Feature({"AndroidWebView", "Android-JavaBridge"})
public void testAddJavascriptInterfaceIsSafeByDefault() throws Throwable {
class Test {
- public String blocked() { return "bar"; }
+ public String blocked() {
+ return "bar";
+ }
@JavascriptInterface
- public String allowed() { return "bar"; }
+ public String allowed() {
+ return "bar";
+ }
}
// Manually inject the Test object, making sure to use the
public void testObjectsInspection() throws Throwable {
class Test {
@JavascriptInterface
- public String m1() { return "foo"; }
+ public String m1() {
+ return "foo";
+ }
@JavascriptInterface
- public String m2() { return "bar"; }
+ public String m2() {
+ return "bar";
+ }
@JavascriptInterface
- public String m2(int x) { return "bar " + x; }
+ public String m2(int x) {
+ return "bar " + x;
+ }
}
final String jsObjectKeysTestTemplate = "Object.keys(%s).toString()";
final String jsForInTestTemplate =
- "(function(){" +
- " var s=[]; for(var m in %s) s.push(m); return s.join(\",\")" +
- "})()";
+ "(function(){"
+ + " var s=[]; for(var m in %s) s.push(m); return s.join(\",\")"
+ + "})()";
final String inspectableObjectName = "testObj1";
final String nonInspectableObjectName = "testObj2";
assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.getClass"));
assertRaisesException("testObject.getClass()");
}
+
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-JavaBridge"})
+ public void testReplaceJavascriptInterface() throws Throwable {
+ class Test {
+ public Test(int value) {
+ mValue = value;
+ }
+ @JavascriptInterface
+ public int getValue() {
+ return mValue;
+ }
+ private int mValue;
+ }
+ injectObjectAndReload(new Test(13), "testObject");
+ assertEquals("13", executeJavaScriptAndGetStringResult("testObject.getValue()"));
+ // The documentation doesn't specify, what happens if the embedder is trying
+ // to inject a different object under the same name. The current implementation
+ // simply replaces the old object with the new one.
+ injectObjectAndReload(new Test(42), "testObject");
+ assertEquals("42", executeJavaScriptAndGetStringResult("testObject.getValue()"));
+ }
}