1 // Copyright 2017 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.content.browser;
6 
7 import org.junit.Assert;
8 import org.junit.runner.Description;
9 import org.junit.runners.model.Statement;
10 
11 import org.chromium.base.Log;
12 import org.chromium.base.test.params.ParameterProvider;
13 import org.chromium.base.test.params.ParameterSet;
14 import org.chromium.base.test.util.UrlUtils;
15 import org.chromium.content_public.browser.JavascriptInjector;
16 import org.chromium.content_public.browser.LoadUrlParams;
17 import org.chromium.content_public.browser.WebContents;
18 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
19 import org.chromium.content_shell_apk.ContentShellActivity;
20 import org.chromium.content_shell_apk.ContentShellActivityTestRule;
21 
22 import java.lang.annotation.Annotation;
23 import java.util.Arrays;
24 import java.util.List;
25 
26 /**
27  * ActivityTestRule with common functionality for testing the Java Bridge.
28  */
29 public class JavaBridgeActivityTestRule extends ContentShellActivityTestRule {
30     /** Shared name for batched JavaBridge tests. */
31     public static final String BATCH = "JavaBridgeActivityTestRule";
32 
33     /**
34      * {@link ParameterProvider} used for parameterized test that provides the Mojo usage state.
35      */
36     public static class MojoTestParams implements ParameterProvider {
37         private static List<ParameterSet> sMojoTestParams =
38                 Arrays.asList(new ParameterSet().value(false).name("MojoUnused"),
39                         new ParameterSet().value(true).name("MojoUsed"));
40 
41         @Override
getParameters()42         public List<ParameterSet> getParameters() {
43             return sMojoTestParams;
44         }
45     }
46 
47     /**
48      * {@link ParameterProvider} used for parameterized test that keeps the legacy tests.
49      */
50     public static class LegacyTestParams implements ParameterProvider {
51         private static List<ParameterSet> sLegacyTestParams =
52                 Arrays.asList(new ParameterSet().value(false));
53 
54         @Override
getParameters()55         public List<ParameterSet> getParameters() {
56             return sLegacyTestParams;
57         }
58     }
59 
60     private TestCallbackHelperContainer mTestCallbackHelperContainer;
61     private boolean mUseMojo;
62 
63     public static class Controller {
64         private static final int RESULT_WAIT_TIME = 5000;
65 
66         private boolean mIsResultReady;
67 
notifyResultIsReady()68         protected synchronized void notifyResultIsReady() {
69             mIsResultReady = true;
70             notify();
71         }
72 
waitForResult()73         protected synchronized void waitForResult() {
74             while (!mIsResultReady) {
75                 try {
76                     wait(RESULT_WAIT_TIME);
77                 } catch (Exception e) {
78                     continue;
79                 }
80                 if (!mIsResultReady) {
81                     Assert.fail("Wait timed out");
82                 }
83             }
84             mIsResultReady = false;
85         }
86     }
87 
getTestCallBackHelperContainer()88     public TestCallbackHelperContainer getTestCallBackHelperContainer() {
89         return mTestCallbackHelperContainer;
90     }
91 
92     /**
93      * Sets up the ContentView. Intended to be called from setUp().
94      */
setUpContentView()95     public void setUpContentView() {
96         // This starts the activity, so must be called on the test thread.
97         final ContentShellActivity activity = launchContentShellWithUrl(
98                 UrlUtils.encodeHtmlDataUri("<html><head></head><body>test</body></html>"));
99         waitForActiveShellToBeDoneLoading();
100 
101         try {
102             runOnUiThread(new Runnable() {
103                 @Override
104                 public void run() {
105                     mTestCallbackHelperContainer =
106                             new TestCallbackHelperContainer(activity.getActiveWebContents());
107                 }
108             });
109         } catch (Throwable e) {
110             throw new RuntimeException(
111                     "Failed to set up ContentView: " + Log.getStackTraceString(e));
112         }
113     }
114 
executeJavaScript(String script)115     public void executeJavaScript(String script) throws Throwable {
116         runOnUiThread(new Runnable() {
117             @Override
118             public void run() {
119                 // When a JavaScript URL is executed, if the value of the last
120                 // expression evaluated is not 'undefined', this value is
121                 // converted to a string and used as the new document for the
122                 // frame. We don't want this behaviour, so wrap the script in
123                 // an anonymous function.
124                 getWebContents().getNavigationController().loadUrl(
125                         new LoadUrlParams("javascript:(function() { " + script + " })()"));
126             }
127         });
128     }
129 
injectObjectAndReload(Object object, String name)130     public void injectObjectAndReload(Object object, String name) {
131         injectObjectAndReload(object, name, null);
132     }
133 
injectObjectAndReload( Object object, String name, Class<? extends Annotation> requiredAnnotation)134     public void injectObjectAndReload(
135             Object object, String name, Class<? extends Annotation> requiredAnnotation) {
136         injectObjectsAndReload(object, name, null, null, requiredAnnotation);
137     }
138 
injectObjectsAndReload(final Object object1, final String name1, final Object object2, final String name2, final Class<? extends Annotation> requiredAnnotation)139     public void injectObjectsAndReload(final Object object1, final String name1,
140             final Object object2, final String name2,
141             final Class<? extends Annotation> requiredAnnotation) {
142         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
143                 mTestCallbackHelperContainer.getOnPageFinishedHelper();
144         int currentCallCount = onPageFinishedHelper.getCallCount();
145         try {
146             runOnUiThread(new Runnable() {
147                 @Override
148                 public void run() {
149                     WebContents webContents = getWebContents();
150                     JavascriptInjector injector =
151                             JavascriptInjector.fromWebContents(webContents, mUseMojo);
152                     injector.addPossiblyUnsafeInterface(object1, name1, requiredAnnotation);
153                     if (object2 != null && name2 != null) {
154                         injector.addPossiblyUnsafeInterface(object2, name2, requiredAnnotation);
155                     }
156                     webContents.getNavigationController().reload(true);
157                 }
158             });
159             onPageFinishedHelper.waitForCallback(currentCallCount);
160         } catch (Throwable e) {
161             throw new RuntimeException(
162                     "Failed to injectObjectsAndReload: " + Log.getStackTraceString(e));
163         }
164     }
setupMojoTest(boolean useMojo)165     public void setupMojoTest(boolean useMojo) {
166         mUseMojo = useMojo;
167     }
168 
synchronousPageReload()169     public void synchronousPageReload() throws Throwable {
170         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
171                 mTestCallbackHelperContainer.getOnPageFinishedHelper();
172         int currentCallCount = onPageFinishedHelper.getCallCount();
173         runOnUiThread(new Runnable() {
174             @Override
175             public void run() {
176                 getWebContents().getNavigationController().reload(true);
177             }
178         });
179         onPageFinishedHelper.waitForCallback(currentCallCount);
180     }
181 
182 
183     @Override
apply(Statement base, Description desc)184     public Statement apply(Statement base, Description desc) {
185         setUpContentView();
186         return super.apply(base, desc);
187     }
188 }
189