JavaBridgeBasicsTest.java revision 116680a4aac90f2aa7413d9095a592090648e557
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)package org.chromium.content.browser; 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.test.suitebuilder.annotation.SmallTest; 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.base.test.util.Feature; 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import org.chromium.content.browser.test.util.TestCallbackHelperContainer; 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.content_shell_apk.ContentShellActivity; 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.lang.annotation.Annotation; 1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import java.lang.annotation.ElementType; 15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import java.lang.annotation.Retention; 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.lang.annotation.RetentionPolicy; 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.lang.annotation.Target; 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.lang.ref.WeakReference; 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Part of the test suite for the Java Bridge. Tests a number of features including ... 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - The type of injected objects 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - The type of their methods 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Replacing objects 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Removing objects 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Access control 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Calling methods on returned objects 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Multiply injected objects 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Threading 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * - Inheritance 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public class JavaBridgeBasicsTest extends JavaBridgeTestBase { 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private class TestController extends Controller { 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private int mIntValue; 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private long mLongValue; 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private String mStringValue; 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mBooleanValue; 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public synchronized void setIntValue(int x) { 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mIntValue = x; 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyResultIsReady(); 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized void setLongValue(long x) { 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mLongValue = x; 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyResultIsReady(); 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public synchronized void setStringValue(String x) { 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mStringValue = x; 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyResultIsReady(); 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized void setBooleanValue(boolean x) { 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mBooleanValue = x; 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyResultIsReady(); 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized int waitForIntValue() { 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) waitForResult(); 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return mIntValue; 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized long waitForLongValue() { 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) waitForResult(); 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return mLongValue; 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized String waitForStringValue() { 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) waitForResult(); 66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return mStringValue; 67a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch } 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public synchronized boolean waitForBooleanValue() { 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) waitForResult(); 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return mBooleanValue; 71a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch } 72a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 73a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch public synchronized String getStringValue() { 74a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch return mStringValue; 75a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch } 76a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch } 77a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 78a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch private static class ObjectWithStaticMethod { 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public static String staticMethod() { 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return "foo"; 81a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch } 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) TestController mTestController; 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected void setUp() throws Exception { 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) super.setUp(); 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mTestController = new TestController(); 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) setUpContentView(mTestController, "testController"); 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) protected ContentShellActivity launchContentShellWithUrl(String url) { 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Expose a global function "gc()" into pages. 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return launchContentShellWithUrlAndCommandLineArgs( 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) url, new String[]{ "--js-flags=--expose-gc" }); 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Note that this requires that we can pass a JavaScript string to Java. 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected String executeJavaScriptAndGetStringResult(String script) throws Throwable { 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) executeJavaScript("testController.setStringValue(" + script + ");"); 10358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return mTestController.waitForStringValue(); 10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) } 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected void injectObjectAndReload(final Object object, final String name) throws Throwable { 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) injectObjectAndReload(object, name, null); 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected void injectObjectAndReload(final Object object, final String name, 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) final Class<? extends Annotation> requiredAnnotation) throws Throwable { 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mTestCallbackHelperContainer.getOnPageFinishedHelper(); 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int currentCallCount = onPageFinishedHelper.getCallCount(); 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) runTestOnUiThread(new Runnable() { 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void run() { 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) getContentViewCore().addPossiblyUnsafeJavascriptInterface(object, 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) name, requiredAnnotation); 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) getContentViewCore().reload(true); 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 123a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch onPageFinishedHelper.waitForCallback(currentCallCount); 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected void synchronousPageReload() throws Throwable { 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mTestCallbackHelperContainer.getOnPageFinishedHelper(); 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int currentCallCount = onPageFinishedHelper.getCallCount(); 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) runTestOnUiThread(new Runnable() { 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void run() { 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) getContentViewCore().reload(true); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) onPageFinishedHelper.waitForCallback(currentCallCount); 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Note that this requires that we can pass a JavaScript boolean to Java. 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private void assertRaisesException(String script) throws Throwable { 141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) executeJavaScript("try {" + 142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) script + ";" + 143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " testController.setBooleanValue(false);" + 144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "} catch (exception) {" + 145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch " testController.setBooleanValue(true);" + 146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "}"); 147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) assertTrue(mTestController.waitForBooleanValue()); 148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) @SmallTest 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Feature({"AndroidWebView", "Android-JavaBridge"}) 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void testTypeOfInjectedObject() throws Throwable { 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @SmallTest 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) @Feature({"AndroidWebView", "Android-JavaBridge"}) 158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public void testAdditionNotReflectedUntilReload() throws Throwable { 159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); 160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) runTestOnUiThread(new Runnable() { 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void run() { 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) getContentViewCore().addPossiblyUnsafeJavascriptInterface( 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) new Object(), "testObject", null); 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) synchronousPageReload(); 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @SmallTest 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Feature({"AndroidWebView", "Android-JavaBridge"}) 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void testRemovalNotReflectedUntilReload() throws Throwable { 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) injectObjectAndReload(new Object(), "testObject"); 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); 1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) runTestOnUiThread(new Runnable() { 1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void run() { 1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) getContentViewCore().removeJavascriptInterface("testObject"); 1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) synchronousPageReload(); 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @SmallTest 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Feature({"AndroidWebView", "Android-JavaBridge"}) 190 public void testRemoveObjectNotAdded() throws Throwable { 191 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 192 mTestCallbackHelperContainer.getOnPageFinishedHelper(); 193 int currentCallCount = onPageFinishedHelper.getCallCount(); 194 runTestOnUiThread(new Runnable() { 195 @Override 196 public void run() { 197 getContentViewCore().removeJavascriptInterface("foo"); 198 getContentViewCore().reload(true); 199 } 200 }); 201 onPageFinishedHelper.waitForCallback(currentCallCount); 202 assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof foo")); 203 } 204 205 @SmallTest 206 @Feature({"AndroidWebView", "Android-JavaBridge"}) 207 public void testTypeOfMethod() throws Throwable { 208 assertEquals("function", 209 executeJavaScriptAndGetStringResult("typeof testController.setStringValue")); 210 } 211 212 @SmallTest 213 @Feature({"AndroidWebView", "Android-JavaBridge"}) 214 public void testTypeOfInvalidMethod() throws Throwable { 215 assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testController.foo")); 216 } 217 218 @SmallTest 219 @Feature({"AndroidWebView", "Android-JavaBridge"}) 220 public void testCallingInvalidMethodRaisesException() throws Throwable { 221 assertRaisesException("testController.foo()"); 222 } 223 224 @SmallTest 225 @Feature({"AndroidWebView", "Android-JavaBridge"}) 226 public void testUncaughtJavaExceptionRaisesJavaScriptException() throws Throwable { 227 injectObjectAndReload(new Object() { 228 public void method() { throw new RuntimeException("foo"); } 229 }, "testObject"); 230 assertRaisesException("testObject.method()"); 231 } 232 233 // Note that this requires that we can pass a JavaScript string to Java. 234 @SmallTest 235 @Feature({"AndroidWebView", "Android-JavaBridge"}) 236 public void testTypeOfStaticMethod() throws Throwable { 237 injectObjectAndReload(new ObjectWithStaticMethod(), "testObject"); 238 executeJavaScript("testController.setStringValue(typeof testObject.staticMethod)"); 239 assertEquals("function", mTestController.waitForStringValue()); 240 } 241 242 // Note that this requires that we can pass a JavaScript string to Java. 243 @SmallTest 244 @Feature({"AndroidWebView", "Android-JavaBridge"}) 245 public void testCallStaticMethod() throws Throwable { 246 injectObjectAndReload(new ObjectWithStaticMethod(), "testObject"); 247 executeJavaScript("testController.setStringValue(testObject.staticMethod())"); 248 assertEquals("foo", mTestController.waitForStringValue()); 249 } 250 251 @SmallTest 252 @Feature({"AndroidWebView", "Android-JavaBridge"}) 253 public void testPrivateMethodNotExposed() throws Throwable { 254 injectObjectAndReload(new Object() { 255 private void method() {} 256 protected void method2() {} 257 }, "testObject"); 258 assertEquals("undefined", 259 executeJavaScriptAndGetStringResult("typeof testObject.method")); 260 assertEquals("undefined", 261 executeJavaScriptAndGetStringResult("typeof testObject.method2")); 262 } 263 264 @SmallTest 265 @Feature({"AndroidWebView", "Android-JavaBridge"}) 266 public void testReplaceInjectedObject() throws Throwable { 267 injectObjectAndReload(new Object() { 268 public void method() { mTestController.setStringValue("object 1"); } 269 }, "testObject"); 270 executeJavaScript("testObject.method()"); 271 assertEquals("object 1", mTestController.waitForStringValue()); 272 273 injectObjectAndReload(new Object() { 274 public void method() { mTestController.setStringValue("object 2"); } 275 }, "testObject"); 276 executeJavaScript("testObject.method()"); 277 assertEquals("object 2", mTestController.waitForStringValue()); 278 } 279 280 @SmallTest 281 @Feature({"AndroidWebView", "Android-JavaBridge"}) 282 public void testInjectNullObjectIsIgnored() throws Throwable { 283 injectObjectAndReload(null, "testObject"); 284 assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); 285 } 286 287 @SmallTest 288 @Feature({"AndroidWebView", "Android-JavaBridge"}) 289 public void testReplaceInjectedObjectWithNullObjectIsIgnored() throws Throwable { 290 injectObjectAndReload(new Object(), "testObject"); 291 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); 292 injectObjectAndReload(null, "testObject"); 293 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); 294 } 295 296 @SmallTest 297 @Feature({"AndroidWebView", "Android-JavaBridge"}) 298 public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable { 299 injectObjectAndReload(new Object() { 300 public void method() { mTestController.setStringValue("0 args"); } 301 public void method(int x) { mTestController.setStringValue("1 arg"); } 302 public void method(int x, int y) { mTestController.setStringValue("2 args"); } 303 }, "testObject"); 304 executeJavaScript("testObject.method()"); 305 assertEquals("0 args", mTestController.waitForStringValue()); 306 executeJavaScript("testObject.method(42)"); 307 assertEquals("1 arg", mTestController.waitForStringValue()); 308 executeJavaScript("testObject.method(null)"); 309 assertEquals("1 arg", mTestController.waitForStringValue()); 310 executeJavaScript("testObject.method(undefined)"); 311 assertEquals("1 arg", mTestController.waitForStringValue()); 312 executeJavaScript("testObject.method(42, 42)"); 313 assertEquals("2 args", mTestController.waitForStringValue()); 314 } 315 316 @SmallTest 317 @Feature({"AndroidWebView", "Android-JavaBridge"}) 318 public void testCallMethodWithWrongNumberOfArgumentsRaisesException() throws Throwable { 319 assertRaisesException("testController.setIntValue()"); 320 assertRaisesException("testController.setIntValue(42, 42)"); 321 } 322 323 @SmallTest 324 @Feature({"AndroidWebView", "Android-JavaBridge"}) 325 public void testObjectPersistsAcrossPageLoads() throws Throwable { 326 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); 327 synchronousPageReload(); 328 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); 329 } 330 331 @SmallTest 332 @Feature({"AndroidWebView", "Android-JavaBridge"}) 333 public void testCustomPropertiesCleanedUpOnPageReloads() throws Throwable { 334 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); 335 executeJavaScript("testController.myProperty = 42;"); 336 assertEquals("42", executeJavaScriptAndGetStringResult("testController.myProperty")); 337 synchronousPageReload(); 338 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); 339 assertEquals("undefined", executeJavaScriptAndGetStringResult("testController.myProperty")); 340 } 341 342 @SmallTest 343 @Feature({"AndroidWebView", "Android-JavaBridge"}) 344 public void testSameObjectInjectedMultipleTimes() throws Throwable { 345 class TestObject { 346 private int mNumMethodInvocations; 347 public void method() { mTestController.setIntValue(++mNumMethodInvocations); } 348 } 349 final TestObject testObject = new TestObject(); 350 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 351 mTestCallbackHelperContainer.getOnPageFinishedHelper(); 352 int currentCallCount = onPageFinishedHelper.getCallCount(); 353 runTestOnUiThread(new Runnable() { 354 @Override 355 public void run() { 356 getContentViewCore().addPossiblyUnsafeJavascriptInterface( 357 testObject, "testObject1", null); 358 getContentViewCore().addPossiblyUnsafeJavascriptInterface( 359 testObject, "testObject2", null); 360 getContentViewCore().reload(true); 361 } 362 }); 363 onPageFinishedHelper.waitForCallback(currentCallCount); 364 executeJavaScript("testObject1.method()"); 365 assertEquals(1, mTestController.waitForIntValue()); 366 executeJavaScript("testObject2.method()"); 367 assertEquals(2, mTestController.waitForIntValue()); 368 } 369 370 @SmallTest 371 @Feature({"AndroidWebView", "Android-JavaBridge"}) 372 public void testCallMethodOnReturnedObject() throws Throwable { 373 injectObjectAndReload(new Object() { 374 public Object getInnerObject() { 375 return new Object() { 376 public void method(int x) { mTestController.setIntValue(x); } 377 }; 378 } 379 }, "testObject"); 380 executeJavaScript("testObject.getInnerObject().method(42)"); 381 assertEquals(42, mTestController.waitForIntValue()); 382 } 383 384 @SmallTest 385 @Feature({"AndroidWebView", "Android-JavaBridge"}) 386 public void testReturnedObjectInjectedElsewhere() throws Throwable { 387 class InnerObject { 388 private int mNumMethodInvocations; 389 public void method() { mTestController.setIntValue(++mNumMethodInvocations); } 390 } 391 final InnerObject innerObject = new InnerObject(); 392 final Object object = new Object() { 393 public InnerObject getInnerObject() { 394 return innerObject; 395 } 396 }; 397 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 398 mTestCallbackHelperContainer.getOnPageFinishedHelper(); 399 int currentCallCount = onPageFinishedHelper.getCallCount(); 400 runTestOnUiThread(new Runnable() { 401 @Override 402 public void run() { 403 getContentViewCore().addPossiblyUnsafeJavascriptInterface( 404 object, "testObject", null); 405 getContentViewCore().addPossiblyUnsafeJavascriptInterface( 406 innerObject, "innerObject", null); 407 getContentViewCore().reload(true); 408 } 409 }); 410 onPageFinishedHelper.waitForCallback(currentCallCount); 411 executeJavaScript("testObject.getInnerObject().method()"); 412 assertEquals(1, mTestController.waitForIntValue()); 413 executeJavaScript("innerObject.method()"); 414 assertEquals(2, mTestController.waitForIntValue()); 415 } 416 417 // Verify that Java objects returned from bridge object methods are dereferenced 418 // on the Java side once they have been fully dereferenced on the JS side. 419 // Failing this test would mean that methods returning objects effectively create a memory 420 // leak. 421 @SmallTest 422 @Feature({"AndroidWebView", "Android-JavaBridge"}) 423 public void testReturnedObjectIsGarbageCollected() throws Throwable { 424 // Make sure V8 exposes "gc" property on the global object (enabled with --expose-gc flag) 425 assertEquals("function", executeJavaScriptAndGetStringResult("typeof gc")); 426 class InnerObject { 427 } 428 class TestObject { 429 public InnerObject getInnerObject() { 430 InnerObject inner = new InnerObject(); 431 weakRefForInner = new WeakReference<InnerObject>(inner); 432 return inner; 433 } 434 // A weak reference is used to check InnerObject instance reachability. 435 WeakReference<InnerObject> weakRefForInner; 436 } 437 TestObject object = new TestObject(); 438 injectObjectAndReload(object, "testObject"); 439 // Initially, store a reference to the inner object in JS to make sure it's not 440 // garbage-collected prematurely. 441 assertEquals("object", executeJavaScriptAndGetStringResult( 442 "(function() { " + 443 "globalInner = testObject.getInnerObject(); return typeof globalInner; " + 444 "})()")); 445 assertTrue(object.weakRefForInner.get() != null); 446 // Check that returned Java object is being held by the Java bridge, thus it's not 447 // collected. Note that despite that what JavaDoc says about invoking "gc()", both Dalvik 448 // and ART actually run the collector. 449 Runtime.getRuntime().gc(); 450 assertTrue(object.weakRefForInner.get() != null); 451 // Now dereference the inner object in JS and run GC to collect the interface object. 452 assertEquals("true", executeJavaScriptAndGetStringResult( 453 "(function() { " + 454 "delete globalInner; gc(); return (typeof globalInner == 'undefined'); " + 455 "})()")); 456 // Force GC on the Java side again. The bridge had to release the inner object, so it must 457 // be collected this time. 458 Runtime.getRuntime().gc(); 459 assertEquals(null, object.weakRefForInner.get()); 460 } 461 462 @SmallTest 463 @Feature({"AndroidWebView", "Android-JavaBridge"}) 464 public void testSameReturnedObjectUsesSameWrapper() throws Throwable { 465 class InnerObject { 466 } 467 final InnerObject innerObject = new InnerObject(); 468 final Object injectedTestObject = new Object() { 469 public InnerObject getInnerObject() { 470 return innerObject; 471 } 472 }; 473 injectObjectAndReload(injectedTestObject, "injectedTestObject"); 474 executeJavaScript("inner1 = injectedTestObject.getInnerObject()"); 475 executeJavaScript("inner2 = injectedTestObject.getInnerObject()"); 476 assertEquals("object", executeJavaScriptAndGetStringResult("typeof inner1")); 477 assertEquals("object", executeJavaScriptAndGetStringResult("typeof inner2")); 478 assertEquals("true", executeJavaScriptAndGetStringResult("inner1 === inner2")); 479 } 480 481 @SmallTest 482 @Feature({"AndroidWebView", "Android-JavaBridge"}) 483 public void testMethodInvokedOnBackgroundThread() throws Throwable { 484 injectObjectAndReload(new Object() { 485 public void captureThreadId() { 486 mTestController.setLongValue(Thread.currentThread().getId()); 487 } 488 }, "testObject"); 489 executeJavaScript("testObject.captureThreadId()"); 490 final long threadId = mTestController.waitForLongValue(); 491 assertFalse(threadId == Thread.currentThread().getId()); 492 runTestOnUiThread(new Runnable() { 493 @Override 494 public void run() { 495 assertFalse(threadId == Thread.currentThread().getId()); 496 } 497 }); 498 } 499 500 @SmallTest 501 @Feature({"AndroidWebView", "Android-JavaBridge"}) 502 public void testPublicInheritedMethod() throws Throwable { 503 class Base { 504 public void method(int x) { mTestController.setIntValue(x); } 505 } 506 class Derived extends Base { 507 } 508 injectObjectAndReload(new Derived(), "testObject"); 509 assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.method")); 510 executeJavaScript("testObject.method(42)"); 511 assertEquals(42, mTestController.waitForIntValue()); 512 } 513 514 @SmallTest 515 @Feature({"AndroidWebView", "Android-JavaBridge"}) 516 public void testPrivateInheritedMethod() throws Throwable { 517 class Base { 518 private void method() {} 519 } 520 class Derived extends Base { 521 } 522 injectObjectAndReload(new Derived(), "testObject"); 523 assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject.method")); 524 } 525 526 @SmallTest 527 @Feature({"AndroidWebView", "Android-JavaBridge"}) 528 public void testOverriddenMethod() throws Throwable { 529 class Base { 530 public void method() { mTestController.setStringValue("base"); } 531 } 532 class Derived extends Base { 533 @Override 534 public void method() { mTestController.setStringValue("derived"); } 535 } 536 injectObjectAndReload(new Derived(), "testObject"); 537 executeJavaScript("testObject.method()"); 538 assertEquals("derived", mTestController.waitForStringValue()); 539 } 540 541 @SmallTest 542 @Feature({"AndroidWebView", "Android-JavaBridge"}) 543 public void testEnumerateMembers() throws Throwable { 544 injectObjectAndReload(new Object() { 545 public void method() {} 546 private void privateMethod() {} 547 public int field; 548 private int privateField; 549 }, "testObject"); 550 executeJavaScript( 551 "var result = \"\"; " + 552 "for (x in testObject) { result += \" \" + x } " + 553 "testController.setStringValue(result);"); 554 assertEquals(" equals getClass hashCode method notify notifyAll toString wait", 555 mTestController.waitForStringValue()); 556 } 557 558 @SmallTest 559 @Feature({"AndroidWebView", "Android-JavaBridge"}) 560 public void testReflectPublicMethod() throws Throwable { 561 injectObjectAndReload(new Object() { 562 public Class<?> myGetClass() { return getClass(); } 563 public String method() { return "foo"; } 564 }, "testObject"); 565 assertEquals("foo", executeJavaScriptAndGetStringResult( 566 "testObject.myGetClass().getMethod('method', null).invoke(testObject, null)" + 567 ".toString()")); 568 } 569 570 @SmallTest 571 @Feature({"AndroidWebView", "Android-JavaBridge"}) 572 public void testReflectPublicField() throws Throwable { 573 injectObjectAndReload(new Object() { 574 public Class<?> myGetClass() { return getClass(); } 575 public String field = "foo"; 576 }, "testObject"); 577 assertEquals("foo", executeJavaScriptAndGetStringResult( 578 "testObject.myGetClass().getField('field').get(testObject).toString()")); 579 } 580 581 @SmallTest 582 @Feature({"AndroidWebView", "Android-JavaBridge"}) 583 public void testReflectPrivateMethodRaisesException() throws Throwable { 584 injectObjectAndReload(new Object() { 585 public Class<?> myGetClass() { return getClass(); } 586 private void method() {}; 587 }, "testObject"); 588 assertRaisesException("testObject.myGetClass().getMethod('method', null)"); 589 // getDeclaredMethod() is able to access a private method, but invoke() 590 // throws a Java exception. 591 assertRaisesException( 592 "testObject.myGetClass().getDeclaredMethod('method', null)." + 593 "invoke(testObject, null)"); 594 } 595 596 @SmallTest 597 @Feature({"AndroidWebView", "Android-JavaBridge"}) 598 public void testReflectPrivateFieldRaisesException() throws Throwable { 599 injectObjectAndReload(new Object() { 600 public Class<?> myGetClass() { return getClass(); } 601 private int field; 602 }, "testObject"); 603 assertRaisesException("testObject.myGetClass().getField('field')"); 604 // getDeclaredField() is able to access a private field, but getInt() 605 // throws a Java exception. 606 assertRaisesException( 607 "testObject.myGetClass().getDeclaredField('field').getInt(testObject)"); 608 } 609 610 @SmallTest 611 @Feature({"AndroidWebView", "Android-JavaBridge"}) 612 public void testAllowNonAnnotatedMethods() throws Throwable { 613 injectObjectAndReload(new Object() { 614 public String allowed() { return "foo"; } 615 }, "testObject", null); 616 617 // Test calling a method of an explicitly inherited class (Base#allowed()). 618 assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.allowed()")); 619 620 // Test calling a method of an implicitly inherited class (Object#toString()). 621 assertEquals("string", executeJavaScriptAndGetStringResult("typeof testObject.toString()")); 622 } 623 624 @SmallTest 625 @Feature({"AndroidWebView", "Android-JavaBridge"}) 626 public void testAllowOnlyAnnotatedMethods() throws Throwable { 627 injectObjectAndReload(new Object() { 628 @JavascriptInterface 629 public String allowed() { return "foo"; } 630 631 public String disallowed() { return "bar"; } 632 }, "testObject", JavascriptInterface.class); 633 634 // getClass() is an Object method and does not have the @JavascriptInterface annotation and 635 // should not be able to be called. 636 assertRaisesException("testObject.getClass()"); 637 assertEquals("undefined", executeJavaScriptAndGetStringResult( 638 "typeof testObject.getClass")); 639 640 // allowed() is marked with the @JavascriptInterface annotation and should be allowed to be 641 // called. 642 assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.allowed()")); 643 644 // disallowed() is not marked with the @JavascriptInterface annotation and should not be 645 // able to be called. 646 assertRaisesException("testObject.disallowed()"); 647 assertEquals("undefined", executeJavaScriptAndGetStringResult( 648 "typeof testObject.disallowed")); 649 } 650 651 @SmallTest 652 @Feature({"AndroidWebView", "Android-JavaBridge"}) 653 public void testAnnotationRequirementRetainsPropertyAcrossObjects() throws Throwable { 654 class Test { 655 @JavascriptInterface 656 public String safe() { return "foo"; } 657 658 public String unsafe() { return "bar"; } 659 } 660 661 class TestReturner { 662 @JavascriptInterface 663 public Test getTest() { return new Test(); } 664 } 665 666 // First test with safe mode off. 667 injectObjectAndReload(new TestReturner(), "unsafeTestObject", null); 668 669 // safe() should be able to be called regardless of whether or not we are in safe mode. 670 assertEquals("foo", executeJavaScriptAndGetStringResult( 671 "unsafeTestObject.getTest().safe()")); 672 // unsafe() should be able to be called because we are not in safe mode. 673 assertEquals("bar", executeJavaScriptAndGetStringResult( 674 "unsafeTestObject.getTest().unsafe()")); 675 676 // Now test with safe mode on. 677 injectObjectAndReload(new TestReturner(), "safeTestObject", JavascriptInterface.class); 678 679 // safe() should be able to be called regardless of whether or not we are in safe mode. 680 assertEquals("foo", executeJavaScriptAndGetStringResult( 681 "safeTestObject.getTest().safe()")); 682 // unsafe() should not be able to be called because we are in safe mode. 683 assertRaisesException("safeTestObject.getTest().unsafe()"); 684 assertEquals("undefined", executeJavaScriptAndGetStringResult( 685 "typeof safeTestObject.getTest().unsafe")); 686 // getClass() is an Object method and does not have the @JavascriptInterface annotation and 687 // should not be able to be called. 688 assertRaisesException("safeTestObject.getTest().getClass()"); 689 assertEquals("undefined", executeJavaScriptAndGetStringResult( 690 "typeof safeTestObject.getTest().getClass")); 691 } 692 693 @SmallTest 694 @Feature({"AndroidWebView", "Android-JavaBridge"}) 695 public void testAnnotationDoesNotGetInherited() throws Throwable { 696 class Base { 697 @JavascriptInterface 698 public void base() { } 699 } 700 701 class Child extends Base { 702 @Override 703 public void base() { } 704 } 705 706 injectObjectAndReload(new Child(), "testObject", JavascriptInterface.class); 707 708 // base() is inherited. The inherited method does not have the @JavascriptInterface 709 // annotation and should not be able to be called. 710 assertRaisesException("testObject.base()"); 711 assertEquals("undefined", executeJavaScriptAndGetStringResult( 712 "typeof testObject.base")); 713 } 714 715 @SuppressWarnings("javadoc") 716 @Retention(RetentionPolicy.RUNTIME) 717 @Target({ElementType.METHOD}) 718 @interface TestAnnotation { 719 } 720 721 @SmallTest 722 @Feature({"AndroidWebView", "Android-JavaBridge"}) 723 public void testCustomAnnotationRestriction() throws Throwable { 724 class Test { 725 @TestAnnotation 726 public String checkTestAnnotationFoo() { return "bar"; } 727 728 @JavascriptInterface 729 public String checkJavascriptInterfaceFoo() { return "bar"; } 730 } 731 732 // Inject javascriptInterfaceObj and require the JavascriptInterface annotation. 733 injectObjectAndReload(new Test(), "javascriptInterfaceObj", JavascriptInterface.class); 734 735 // Test#testAnnotationFoo() should fail, as it isn't annotated with JavascriptInterface. 736 assertRaisesException("javascriptInterfaceObj.checkTestAnnotationFoo()"); 737 assertEquals("undefined", executeJavaScriptAndGetStringResult( 738 "typeof javascriptInterfaceObj.checkTestAnnotationFoo")); 739 740 // Test#javascriptInterfaceFoo() should pass, as it is annotated with JavascriptInterface. 741 assertEquals("bar", executeJavaScriptAndGetStringResult( 742 "javascriptInterfaceObj.checkJavascriptInterfaceFoo()")); 743 744 // Inject testAnnotationObj and require the TestAnnotation annotation. 745 injectObjectAndReload(new Test(), "testAnnotationObj", TestAnnotation.class); 746 747 // Test#testAnnotationFoo() should pass, as it is annotated with TestAnnotation. 748 assertEquals("bar", executeJavaScriptAndGetStringResult( 749 "testAnnotationObj.checkTestAnnotationFoo()")); 750 751 // Test#javascriptInterfaceFoo() should fail, as it isn't annotated with TestAnnotation. 752 assertRaisesException("testAnnotationObj.checkJavascriptInterfaceFoo()"); 753 assertEquals("undefined", executeJavaScriptAndGetStringResult( 754 "typeof testAnnotationObj.checkJavascriptInterfaceFoo")); 755 } 756 757 @SmallTest 758 @Feature({"AndroidWebView", "Android-JavaBridge"}) 759 public void testAddJavascriptInterfaceIsSafeByDefault() throws Throwable { 760 class Test { 761 public String blocked() { return "bar"; } 762 763 @JavascriptInterface 764 public String allowed() { return "bar"; } 765 } 766 767 // Manually inject the Test object, making sure to use the 768 // ContentViewCore#addJavascriptInterface, not the possibly unsafe version. 769 TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = 770 mTestCallbackHelperContainer.getOnPageFinishedHelper(); 771 int currentCallCount = onPageFinishedHelper.getCallCount(); 772 runTestOnUiThread(new Runnable() { 773 @Override 774 public void run() { 775 getContentViewCore().addJavascriptInterface(new Test(), 776 "testObject"); 777 getContentViewCore().reload(true); 778 } 779 }); 780 onPageFinishedHelper.waitForCallback(currentCallCount); 781 782 // Test#allowed() should pass, as it is annotated with JavascriptInterface. 783 assertEquals("bar", executeJavaScriptAndGetStringResult( 784 "testObject.allowed()")); 785 786 // Test#blocked() should fail, as it isn't annotated with JavascriptInterface. 787 assertRaisesException("testObject.blocked()"); 788 assertEquals("undefined", executeJavaScriptAndGetStringResult( 789 "typeof testObject.blocked")); 790 } 791 792 @SmallTest 793 @Feature({"AndroidWebView", "Android-JavaBridge"}) 794 public void testObjectsInspection() throws Throwable { 795 class Test { 796 @JavascriptInterface 797 public String m1() { return "foo"; } 798 799 @JavascriptInterface 800 public String m2() { return "bar"; } 801 802 @JavascriptInterface 803 public String m2(int x) { return "bar " + x; } 804 } 805 806 final String jsObjectKeysTestTemplate = "Object.keys(%s).toString()"; 807 final String jsForInTestTemplate = 808 "(function(){" + 809 " var s=[]; for(var m in %s) s.push(m); return s.join(\",\")" + 810 "})()"; 811 final String inspectableObjectName = "testObj1"; 812 final String nonInspectableObjectName = "testObj2"; 813 814 // Inspection is enabled by default. 815 injectObjectAndReload(new Test(), inspectableObjectName, JavascriptInterface.class); 816 817 assertEquals("m1,m2", executeJavaScriptAndGetStringResult( 818 String.format(jsObjectKeysTestTemplate, inspectableObjectName))); 819 assertEquals("m1,m2", executeJavaScriptAndGetStringResult( 820 String.format(jsForInTestTemplate, inspectableObjectName))); 821 822 runTestOnUiThread(new Runnable() { 823 @Override 824 public void run() { 825 getContentViewCore().setAllowJavascriptInterfacesInspection(false); 826 } 827 }); 828 829 injectObjectAndReload(new Test(), nonInspectableObjectName, JavascriptInterface.class); 830 831 assertEquals("", executeJavaScriptAndGetStringResult( 832 String.format(jsObjectKeysTestTemplate, nonInspectableObjectName))); 833 assertEquals("", executeJavaScriptAndGetStringResult( 834 String.format(jsForInTestTemplate, nonInspectableObjectName))); 835 } 836 837 @SmallTest 838 @Feature({"AndroidWebView", "Android-JavaBridge"}) 839 public void testAccessToObjectGetClassIsBlocked() throws Throwable { 840 injectObjectAndReload(new Object(), "testObject"); 841 assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.getClass")); 842 assertRaisesException("testObject.getClass()"); 843 } 844} 845