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