JavaBridgeBasicsTest.java revision f7e26448c3591989840efa65f4f3f93a294b991e
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * Part of the test suite for the WebView's Java Bridge. Tests a number of features including ...
19 * - The type of injected objects
20 * - The type of their methods
21 * - Replacing objects
22 * - Removing objects
23 * - Access control
24 * - Calling methods on returned objects
25 * - Multiply injected objects
26 * - Threading
27 * - Inheritance
28 *
29 * To run this test ...
30 *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeBasicsTest \
31 *     com.android.webviewtests/android.test.InstrumentationTestRunner
32 */
33
34package com.android.webviewtests;
35
36public class JavaBridgeBasicsTest extends JavaBridgeTestBase {
37    private class TestController extends Controller {
38        private int mIntValue;
39        private long mLongValue;
40        private String mStringValue;
41
42        public synchronized void setIntValue(int x) {
43            mIntValue = x;
44            notifyResultIsReady();
45        }
46        public synchronized void setLongValue(long x) {
47            mLongValue = x;
48            notifyResultIsReady();
49        }
50        public synchronized void setStringValue(String x) {
51            mStringValue = x;
52            notifyResultIsReady();
53        }
54
55        public synchronized int waitForIntValue() {
56            waitForResult();
57            return mIntValue;
58        }
59        public synchronized long waitForLongValue() {
60            waitForResult();
61            return mLongValue;
62        }
63        public synchronized String waitForStringValue() {
64            waitForResult();
65            return mStringValue;
66        }
67    }
68
69    TestController mTestController;
70
71    @Override
72    protected void setUp() throws Exception {
73        super.setUp();
74        mTestController = new TestController();
75        setUpWebView(mTestController, "testController");
76    }
77
78    // Note that this requires that we can pass a JavaScript string to Java.
79    protected String executeJavaScriptAndGetStringResult(String script) throws Throwable {
80        executeJavaScript("testController.setStringValue(" + script + ");");
81        return mTestController.waitForStringValue();
82    }
83
84    protected void injectObjectAndReload(final Object object, final String name) throws Throwable {
85        runTestOnUiThread(new Runnable() {
86            @Override
87            public void run() {
88                getWebView().addJavascriptInterface(object, name);
89                getWebView().reload();
90            }
91        });
92        mWebViewClient.waitForOnPageFinished();
93    }
94
95    public void testTypeOfInjectedObject() throws Throwable {
96        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
97    }
98
99    public void testAdditionNotReflectedUntilReload() throws Throwable {
100        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
101        runTestOnUiThread(new Runnable() {
102            @Override
103            public void run() {
104                getWebView().addJavascriptInterface(new Object(), "testObject");
105            }
106        });
107        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
108        runTestOnUiThread(new Runnable() {
109            @Override
110            public void run() {
111                getWebView().reload();
112            }
113        });
114        mWebViewClient.waitForOnPageFinished();
115        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
116    }
117
118    public void testRemovalNotReflectedUntilReload() throws Throwable {
119        injectObjectAndReload(new Object(), "testObject");
120        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
121        runTestOnUiThread(new Runnable() {
122            @Override
123            public void run() {
124                getWebView().removeJavascriptInterface("testObject");
125            }
126        });
127        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
128        runTestOnUiThread(new Runnable() {
129            @Override
130            public void run() {
131                getWebView().reload();
132            }
133        });
134        mWebViewClient.waitForOnPageFinished();
135        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
136    }
137
138    public void testRemoveObjectNotAdded() throws Throwable {
139        runTestOnUiThread(new Runnable() {
140            @Override
141            public void run() {
142                getWebView().removeJavascriptInterface("foo");
143                getWebView().reload();
144            }
145        });
146        mWebViewClient.waitForOnPageFinished();
147        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof foo"));
148    }
149
150    public void testTypeOfMethod() throws Throwable {
151        assertEquals("function",
152                executeJavaScriptAndGetStringResult("typeof testController.setStringValue"));
153    }
154
155    public void testPrivateMethodNotExposed() throws Throwable {
156        injectObjectAndReload(new Object() {
157            private void method() {}
158        }, "testObject");
159        assertEquals("undefined",
160                executeJavaScriptAndGetStringResult("typeof testObject.method"));
161    }
162
163    public void testReplaceInjectedObject() throws Throwable {
164        injectObjectAndReload(new Object() {
165            public void method() { mTestController.setStringValue("object 1"); }
166        }, "testObject");
167        executeJavaScript("testObject.method()");
168        assertEquals("object 1", mTestController.waitForStringValue());
169
170        injectObjectAndReload(new Object() {
171            public void method() { mTestController.setStringValue("object 2"); }
172        }, "testObject");
173        executeJavaScript("testObject.method()");
174        assertEquals("object 2", mTestController.waitForStringValue());
175    }
176
177    public void testInjectNullObjectIsIgnored() throws Throwable {
178        injectObjectAndReload(null, "testObject");
179        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
180    }
181
182    public void testReplaceInjectedObjectWithNullObjectIsIgnored() throws Throwable {
183        injectObjectAndReload(new Object(), "testObject");
184        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
185        injectObjectAndReload(null, "testObject");
186        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
187    }
188
189    public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable {
190        injectObjectAndReload(new Object() {
191            public void method() { mTestController.setStringValue("0 args"); }
192            public void method(int x) { mTestController.setStringValue("1 arg"); }
193            public void method(int x, int y) { mTestController.setStringValue("2 args"); }
194        }, "testObject");
195        executeJavaScript("testObject.method()");
196        assertEquals("0 args", mTestController.waitForStringValue());
197        executeJavaScript("testObject.method(42)");
198        assertEquals("1 arg", mTestController.waitForStringValue());
199        executeJavaScript("testObject.method(null)");
200        assertEquals("1 arg", mTestController.waitForStringValue());
201        executeJavaScript("testObject.method(undefined)");
202        assertEquals("1 arg", mTestController.waitForStringValue());
203        executeJavaScript("testObject.method(42, 42)");
204        assertEquals("2 args", mTestController.waitForStringValue());
205    }
206
207    public void testObjectPersistsAcrossPageLoads() throws Throwable {
208        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
209        runTestOnUiThread(new Runnable() {
210            @Override
211            public void run() {
212                getWebView().reload();
213            }
214        });
215        mWebViewClient.waitForOnPageFinished();
216        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
217    }
218
219    public void testSameObjectInjectedMultipleTimes() throws Throwable {
220        class TestObject {
221            private int mNumMethodInvocations;
222            public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
223        }
224        final TestObject testObject = new TestObject();
225        runTestOnUiThread(new Runnable() {
226            @Override
227            public void run() {
228                getWebView().addJavascriptInterface(testObject, "testObject1");
229                getWebView().addJavascriptInterface(testObject, "testObject2");
230                getWebView().reload();
231            }
232        });
233        mWebViewClient.waitForOnPageFinished();
234        executeJavaScript("testObject1.method()");
235        assertEquals(1, mTestController.waitForIntValue());
236        executeJavaScript("testObject2.method()");
237        assertEquals(2, mTestController.waitForIntValue());
238    }
239
240    public void testCallMethodOnReturnedObject() throws Throwable {
241        injectObjectAndReload(new Object() {
242            public Object getInnerObject() {
243                return new Object() {
244                    public void method(int x) { mTestController.setIntValue(x); }
245                };
246            }
247        }, "testObject");
248        executeJavaScript("testObject.getInnerObject().method(42)");
249        assertEquals(42, mTestController.waitForIntValue());
250    }
251
252    public void testReturnedObjectInjectedElsewhere() throws Throwable {
253        class InnerObject {
254            private int mNumMethodInvocations;
255            public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
256        }
257        final InnerObject innerObject = new InnerObject();
258        final Object object = new Object() {
259            public InnerObject getInnerObject() {
260                return innerObject;
261            }
262        };
263        runTestOnUiThread(new Runnable() {
264            @Override
265            public void run() {
266                getWebView().addJavascriptInterface(object, "testObject");
267                getWebView().addJavascriptInterface(innerObject, "innerObject");
268                getWebView().reload();
269            }
270        });
271        mWebViewClient.waitForOnPageFinished();
272        executeJavaScript("testObject.getInnerObject().method()");
273        assertEquals(1, mTestController.waitForIntValue());
274        executeJavaScript("innerObject.method()");
275        assertEquals(2, mTestController.waitForIntValue());
276    }
277
278    public void testMethodInvokedOnBackgroundThread() throws Throwable {
279        injectObjectAndReload(new Object() {
280            public void captureThreadId() {
281                mTestController.setLongValue(Thread.currentThread().getId());
282            }
283        }, "testObject");
284        executeJavaScript("testObject.captureThreadId()");
285        final long threadId = mTestController.waitForLongValue();
286        assertFalse(threadId == Thread.currentThread().getId());
287        runTestOnUiThread(new Runnable() {
288            @Override
289            public void run() {
290                assertFalse(threadId == Thread.currentThread().getId());
291            }
292        });
293    }
294
295    public void testPublicInheritedMethod() throws Throwable {
296        class Base {
297            public void method(int x) { mTestController.setIntValue(x); }
298        }
299        class Derived extends Base {
300        }
301        injectObjectAndReload(new Derived(), "testObject");
302        assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.method"));
303        executeJavaScript("testObject.method(42)");
304        assertEquals(42, mTestController.waitForIntValue());
305    }
306
307    public void testPrivateInheritedMethod() throws Throwable {
308        class Base {
309            private void method() {}
310        }
311        class Derived extends Base {
312        }
313        injectObjectAndReload(new Derived(), "testObject");
314        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject.method"));
315    }
316
317    public void testOverriddenMethod() throws Throwable {
318        class Base {
319            public void method() { mTestController.setStringValue("base"); }
320        }
321        class Derived extends Base {
322            public void method() { mTestController.setStringValue("derived"); }
323        }
324        injectObjectAndReload(new Derived(), "testObject");
325        executeJavaScript("testObject.method()");
326        assertEquals("derived", mTestController.waitForStringValue());
327    }
328
329    public void testEnumerateMembers() throws Throwable {
330        injectObjectAndReload(new Object() {
331            public void method() {}
332            private void privateMethod() {}
333            public int field;
334            private int privateField;
335        }, "testObject");
336        executeJavaScript(
337                "var result = \"\"; " +
338                "for (x in testObject) { result += \" \" + x } " +
339                "testController.setStringValue(result);");
340        // LIVECONNECT_COMPLIANCE: Should be able to enumerate members.
341        assertEquals("", mTestController.waitForStringValue());
342    }
343}
344