1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.android_webview.test;
6 
7 import android.support.test.InstrumentationRegistry;
8 import android.webkit.JavascriptInterface;
9 
10 import androidx.test.filters.SmallTest;
11 
12 import org.junit.Assert;
13 import org.junit.Before;
14 import org.junit.Rule;
15 import org.junit.Test;
16 import org.junit.runner.RunWith;
17 
18 import org.chromium.android_webview.AwContents;
19 import org.chromium.base.test.util.Feature;
20 import org.chromium.content_public.browser.test.util.TestThreadUtils;
21 
22 /**
23  * Test suite for the WebView specific JavaBridge features.
24  */
25 @RunWith(AwJUnit4ClassRunner.class)
26 public class AwJavaBridgeTest {
27     @Rule
28     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
29 
30     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
31     private AwTestContainerView mTestContainerView;
32 
33     // The system retains a strong ref to the last focused view (in InputMethodManager)
34     // so allow for 1 'leaked' instance.
35     private static final int MAX_IDLE_INSTANCES = 1;
36 
37     @Before
setUp()38     public void setUp() {
39         mTestContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
40     }
41 
42     @Test
43     @SmallTest
44     @Feature({"AndroidWebView", "Android-JavaBridge"})
testDestroyFromJavaObject()45     public void testDestroyFromJavaObject() throws Throwable {
46         final String html = "<html>Hello World</html>";
47         final TestAwContentsClient client2 = new TestAwContentsClient();
48         final AwTestContainerView view2 =
49                 mActivityTestRule.createAwTestContainerViewOnMainSync(client2);
50         final AwContents awContents = mTestContainerView.getAwContents();
51 
52         class Test {
53             @JavascriptInterface
54             public void destroy() {
55                 try {
56                     InstrumentationRegistry.getInstrumentation().runOnMainSync(
57                             () -> awContents.destroy());
58                     // Destroying one AwContents from within the JS callback should still
59                     // leave others functioning. Note that we must do this asynchronously,
60                     // as Blink thread is currently blocked waiting for this method to finish.
61                     mActivityTestRule.loadDataAsync(
62                             view2.getAwContents(), html, "text/html", false);
63                 } catch (Throwable t) {
64                     throw new RuntimeException(t);
65                 }
66             }
67         }
68 
69         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
70         AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents, new Test(), "test");
71 
72         mActivityTestRule.loadDataSync(
73                 awContents, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
74 
75         // Ensure the JS interface object is there, and invoke the test method.
76         Assert.assertEquals("\"function\"",
77                 mActivityTestRule.executeJavaScriptAndWaitForResult(
78                         awContents, mContentsClient, "typeof test.destroy"));
79         int currentCallCount = client2.getOnPageFinishedHelper().getCallCount();
80         TestThreadUtils.runOnUiThreadBlocking(
81                 () -> awContents.evaluateJavaScriptForTests("test.destroy()", null));
82 
83         client2.getOnPageFinishedHelper().waitForCallback(currentCallCount);
84     }
85 
86     @Test
87     @SmallTest
88     @Feature({"AndroidWebView", "Android-JavaBridge"})
testTwoWebViewsCreatedSimultaneously()89     public void testTwoWebViewsCreatedSimultaneously() throws Throwable {
90         final AwContents awContents1 = mTestContainerView.getAwContents();
91         final TestAwContentsClient client2 = new TestAwContentsClient();
92         final AwTestContainerView view2 =
93                 mActivityTestRule.createAwTestContainerViewOnMainSync(client2);
94         final AwContents awContents2 = view2.getAwContents();
95 
96         AwActivityTestRule.enableJavaScriptOnUiThread(awContents1);
97         AwActivityTestRule.enableJavaScriptOnUiThread(awContents2);
98 
99         class Test {
100             Test(int value) {
101                 mValue = value;
102             }
103             @JavascriptInterface
104             public int getValue() {
105                 return mValue;
106             }
107             private int mValue;
108         }
109 
110         AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents1, new Test(1), "test");
111         AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents2, new Test(2), "test");
112         final String html = "<html>Hello World</html>";
113         mActivityTestRule.loadDataSync(
114                 awContents1, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
115         mActivityTestRule.loadDataSync(
116                 awContents2, client2.getOnPageFinishedHelper(), html, "text/html", false);
117 
118         Assert.assertEquals("1",
119                 mActivityTestRule.executeJavaScriptAndWaitForResult(
120                         awContents1, mContentsClient, "test.getValue()"));
121         Assert.assertEquals("2",
122                 mActivityTestRule.executeJavaScriptAndWaitForResult(
123                         awContents2, client2, "test.getValue()"));
124     }
125 
126     @Test
127     @SmallTest
128     @Feature({"AndroidWebView", "Android-JavaBridge"})
testTwoWebViewsSecondCreatedAfterLoadingInFirst()129     public void testTwoWebViewsSecondCreatedAfterLoadingInFirst() throws Throwable {
130         final AwContents awContents1 = mTestContainerView.getAwContents();
131         AwActivityTestRule.enableJavaScriptOnUiThread(awContents1);
132 
133         class Test {
134             Test(int value) {
135                 mValue = value;
136             }
137             @JavascriptInterface
138             public int getValue() {
139                 return mValue;
140             }
141             private int mValue;
142         }
143 
144         AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents1, new Test(1), "test");
145         final String html = "<html>Hello World</html>";
146         mActivityTestRule.loadDataSync(
147                 awContents1, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
148         Assert.assertEquals("1",
149                 mActivityTestRule.executeJavaScriptAndWaitForResult(
150                         awContents1, mContentsClient, "test.getValue()"));
151 
152         final TestAwContentsClient client2 = new TestAwContentsClient();
153         final AwTestContainerView view2 =
154                 mActivityTestRule.createAwTestContainerViewOnMainSync(client2);
155         final AwContents awContents2 = view2.getAwContents();
156         AwActivityTestRule.enableJavaScriptOnUiThread(awContents2);
157 
158         AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents2, new Test(2), "test");
159         mActivityTestRule.loadDataSync(
160                 awContents2, client2.getOnPageFinishedHelper(), html, "text/html", false);
161 
162         Assert.assertEquals("1",
163                 mActivityTestRule.executeJavaScriptAndWaitForResult(
164                         awContents1, mContentsClient, "test.getValue()"));
165         Assert.assertEquals("2",
166                 mActivityTestRule.executeJavaScriptAndWaitForResult(
167                         awContents2, client2, "test.getValue()"));
168     }
169 }
170