1 // Copyright 2019 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.net.Uri; 8 import android.webkit.JavascriptInterface; 9 10 import androidx.test.filters.MediumTest; 11 import androidx.test.filters.SmallTest; 12 13 import org.junit.Assert; 14 import org.junit.Before; 15 import org.junit.ClassRule; 16 import org.junit.Rule; 17 import org.junit.Test; 18 import org.junit.runner.RunWith; 19 20 import org.chromium.android_webview.AwContents; 21 import org.chromium.android_webview.JsReplyProxy; 22 import org.chromium.android_webview.ScriptReference; 23 import org.chromium.android_webview.WebMessageListener; 24 import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedTitleHelper; 25 import org.chromium.android_webview.test.util.CommonResources; 26 import org.chromium.base.test.util.Batch; 27 import org.chromium.base.test.util.Feature; 28 import org.chromium.content_public.browser.MessagePort; 29 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper; 30 import org.chromium.content_public.browser.test.util.TestThreadUtils; 31 import org.chromium.net.test.EmbeddedTestServer; 32 import org.chromium.net.test.EmbeddedTestServerRule; 33 import org.chromium.net.test.util.TestWebServer; 34 35 import java.util.concurrent.LinkedBlockingQueue; 36 37 /** 38 * Test suite for JavaScript Java interaction. 39 */ 40 @RunWith(AwJUnit4ClassRunner.class) 41 @Batch(Batch.PER_CLASS) 42 public class JsJavaInteractionTest { 43 @Rule 44 public AwActivityTestRule mActivityTestRule = new AwActivityTestRule(); 45 @ClassRule 46 public static EmbeddedTestServerRule sTestServerRule = new EmbeddedTestServerRule(); 47 48 private static final String RESOURCE_PATH = "/android_webview/test/data"; 49 private static final String POST_MESSAGE_SIMPLE_HTML = 50 RESOURCE_PATH + "/post_message_simple.html"; 51 private static final String POST_MESSAGE_WITH_PORTS_HTML = 52 RESOURCE_PATH + "/post_message_with_ports.html"; 53 private static final String POST_MESSAGE_REPEAT_HTML = 54 RESOURCE_PATH + "/post_message_repeat.html"; 55 private static final String POST_MESSAGE_REPLY_HTML = 56 RESOURCE_PATH + "/post_message_receives_reply.html"; 57 private static final String FILE_URI = "file:///android_asset/asset_file.html"; 58 private static final String HELLO_WORLD_HTML = RESOURCE_PATH + "/hello_world.html"; 59 60 private static final String HELLO = "Hello"; 61 private static final String NEW_TITLE = "new_title"; 62 private static final String JS_OBJECT_NAME = "myObject"; 63 private static final String JS_OBJECT_NAME_2 = "myObject2"; 64 private static final String DATA_HTML = "<html><body>data</body></html>"; 65 private static final int MESSAGE_COUNT = 10000; 66 67 private EmbeddedTestServer mTestServer; 68 private TestAwContentsClient mContentsClient; 69 private AwContents mAwContents; 70 private TestWebMessageListener mListener; 71 72 private static class TestWebMessageListener implements WebMessageListener { 73 private LinkedBlockingQueue<Data> mQueue = new LinkedBlockingQueue<>(); 74 75 public static class Data { 76 public String mMessage; 77 public Uri mSourceOrigin; 78 public boolean mIsMainFrame; 79 public JsReplyProxy mReplyProxy; 80 public MessagePort[] mPorts; 81 Data(String message, Uri sourceOrigin, boolean isMainFrame, JsReplyProxy replyProxy, MessagePort[] ports)82 public Data(String message, Uri sourceOrigin, boolean isMainFrame, 83 JsReplyProxy replyProxy, MessagePort[] ports) { 84 mMessage = message; 85 mSourceOrigin = sourceOrigin; 86 mIsMainFrame = isMainFrame; 87 mReplyProxy = replyProxy; 88 mPorts = ports; 89 } 90 } 91 92 @Override onPostMessage(String message, Uri sourceOrigin, boolean isMainFrame, JsReplyProxy replyProxy, MessagePort[] ports)93 public void onPostMessage(String message, Uri sourceOrigin, boolean isMainFrame, 94 JsReplyProxy replyProxy, MessagePort[] ports) { 95 mQueue.add(new Data(message, sourceOrigin, isMainFrame, replyProxy, ports)); 96 } 97 waitForOnPostMessage()98 public Data waitForOnPostMessage() throws Exception { 99 return AwActivityTestRule.waitForNextQueueElement(mQueue); 100 } 101 hasNoMoreOnPostMessage()102 public boolean hasNoMoreOnPostMessage() { 103 return mQueue.isEmpty(); 104 } 105 } 106 107 @Before setUp()108 public void setUp() throws Exception { 109 mContentsClient = new TestAwContentsClient(); 110 final AwTestContainerView testContainerView = 111 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 112 mAwContents = testContainerView.getAwContents(); 113 mListener = new TestWebMessageListener(); 114 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true); 115 mTestServer = sTestServerRule.getServer(); 116 } 117 118 @Test 119 @SmallTest 120 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessageSimple()121 public void testPostMessageSimple() throws Throwable { 122 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 123 124 final String url = loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 125 126 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 127 128 assertUrlHasOrigin(url, data.mSourceOrigin); 129 Assert.assertEquals(HELLO, data.mMessage); 130 Assert.assertTrue(data.mIsMainFrame); 131 Assert.assertEquals(0, data.mPorts.length); 132 133 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 134 } 135 136 @Test 137 @SmallTest 138 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessage_LoadData_MessageHasStringNullOrigin()139 public void testPostMessage_LoadData_MessageHasStringNullOrigin() throws Throwable { 140 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 141 142 final String html = "<html><head><script>myObject.postMessage('Hello');</script></head>" 143 + "<body></body></html>"; 144 145 // This uses loadDataAsync() which is equivalent to WebView#loadData(...). 146 mActivityTestRule.loadHtmlSync( 147 mAwContents, mContentsClient.getOnPageFinishedHelper(), html); 148 149 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 150 151 // Note that the source origin is a non-null string of n, u, l, l. 152 Assert.assertNotNull(data.mSourceOrigin); 153 Assert.assertEquals("null", data.mSourceOrigin.toString()); 154 155 Assert.assertEquals(HELLO, data.mMessage); 156 Assert.assertTrue(data.mIsMainFrame); 157 Assert.assertEquals(0, data.mPorts.length); 158 159 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 160 } 161 162 @Test 163 @SmallTest 164 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessageWithPorts()165 public void testPostMessageWithPorts() throws Throwable { 166 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 167 168 final String url = loadUrlFromPath(POST_MESSAGE_WITH_PORTS_HTML); 169 170 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 171 172 final MessagePort[] ports = data.mPorts; 173 assertUrlHasOrigin(url, data.mSourceOrigin); 174 Assert.assertEquals(HELLO, data.mMessage); 175 Assert.assertEquals(1, ports.length); 176 177 // JavaScript code in the page will change the title to NEW_TITLE if postMessage on 178 // this port succeed. 179 final OnReceivedTitleHelper onReceivedTitleHelper = 180 mContentsClient.getOnReceivedTitleHelper(); 181 final int titleCallCount = onReceivedTitleHelper.getCallCount(); 182 ports[0].postMessage(NEW_TITLE, new MessagePort[0]); 183 onReceivedTitleHelper.waitForCallback(titleCallCount); 184 185 Assert.assertEquals(NEW_TITLE, onReceivedTitleHelper.getTitle()); 186 187 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 188 } 189 190 @Test 191 @MediumTest 192 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessageRepeated()193 public void testPostMessageRepeated() throws Throwable { 194 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 195 196 final String url = loadUrlFromPath(POST_MESSAGE_REPEAT_HTML); 197 for (int i = 0; i < MESSAGE_COUNT; ++i) { 198 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 199 assertUrlHasOrigin(url, data.mSourceOrigin); 200 Assert.assertEquals(HELLO + ":" + i, data.mMessage); 201 } 202 203 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 204 } 205 206 @Test 207 @SmallTest 208 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessageFromIframeWorks()209 public void testPostMessageFromIframeWorks() throws Throwable { 210 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 211 212 final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML); 213 final String html = createCrossOriginAccessTestPageHtml(frameUrl); 214 215 // Load a cross origin iframe page. 216 mActivityTestRule.loadDataWithBaseUrlSync(mAwContents, 217 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false, 218 "http://www.google.com", null); 219 220 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 221 222 assertUrlHasOrigin(frameUrl, data.mSourceOrigin); 223 Assert.assertEquals(HELLO, data.mMessage); 224 Assert.assertFalse(data.mIsMainFrame); 225 Assert.assertEquals(0, data.mPorts.length); 226 227 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 228 } 229 230 @Test 231 @SmallTest 232 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListenerAfterPageLoadWontAffectCurrentPage()233 public void testAddWebMessageListenerAfterPageLoadWontAffectCurrentPage() throws Throwable { 234 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 235 236 // Add WebMessageListener after the page loaded won't affect the current page. 237 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 238 239 // Check that we don't have a JavaScript object named JS_OBJECT_NAME 240 Assert.assertFalse(hasJavaScriptObject( 241 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 242 243 // We shouldn't have executed postMessage on JS_OBJECT_NAME either. 244 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 245 } 246 247 @Test 248 @SmallTest 249 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddTheSameWebMessageListenerForDifferentJsObjectsWorks()250 public void testAddTheSameWebMessageListenerForDifferentJsObjectsWorks() throws Throwable { 251 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 252 addWebMessageListenerOnUiThread( 253 mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, mListener); 254 255 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 256 257 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 258 Assert.assertEquals(HELLO, data.mMessage); 259 260 mActivityTestRule.executeJavaScriptAndWaitForResult( 261 mAwContents, mContentsClient, JS_OBJECT_NAME_2 + ".postMessage('" + HELLO + "');"); 262 263 TestWebMessageListener.Data data2 = mListener.waitForOnPostMessage(); 264 Assert.assertEquals(HELLO, data2.mMessage); 265 } 266 267 @Test 268 @SmallTest 269 @Feature({"AndroidWebView", "JsJavaInteraction"}) testFragmentNavigationWontDoJsInjection()270 public void testFragmentNavigationWontDoJsInjection() throws Throwable { 271 String url = loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 272 273 // Add WebMessageListener after the page loaded won't affect the current page. 274 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 275 276 // Load with fragment url. 277 mActivityTestRule.loadUrlSync( 278 mAwContents, mContentsClient.getOnPageFinishedHelper(), url + "#fragment"); 279 280 // Check that we don't have a JavaScript object named JS_OBJECT_NAME 281 Assert.assertFalse(hasJavaScriptObject( 282 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 283 284 // We shouldn't have executed postMessage on JS_OBJECT_NAME either. 285 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 286 } 287 288 @Test 289 @SmallTest 290 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListenerAffectsRendererInitiatedNavigation()291 public void testAddWebMessageListenerAffectsRendererInitiatedNavigation() throws Throwable { 292 // TODO(crbug.com/969842): We'd either replace the following html file with a file contains 293 // no JavaScript code or add a test to ensure that evaluateJavascript() won't 294 // over-trigger DidClearWindowObject. 295 loadUrlFromPath(POST_MESSAGE_WITH_PORTS_HTML); 296 297 // Add WebMessageListener after the page loaded. 298 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 299 300 // Check that we don't have a JavaScript object named JS_OBJECT_NAME 301 Assert.assertFalse(hasJavaScriptObject( 302 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 303 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 304 305 // Navigate to a different web page from renderer and wait until the page loading finished. 306 final String url = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML); 307 final OnPageFinishedHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper(); 308 final int currentCallCount = onPageFinishedHelper.getCallCount(); 309 TestThreadUtils.runOnUiThreadBlocking( 310 () 311 -> mAwContents.evaluateJavaScriptForTests( 312 "window.location.href = '" + url + "';", null)); 313 onPageFinishedHelper.waitForCallback(currentCallCount); 314 315 // We should expect one onPostMessage event. 316 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 317 318 assertUrlHasOrigin(url, data.mSourceOrigin); 319 Assert.assertEquals(HELLO, data.mMessage); 320 Assert.assertEquals(0, data.mPorts.length); 321 322 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 323 } 324 325 @Test 326 @SmallTest 327 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListenerWontAffectOtherAwContents()328 public void testAddWebMessageListenerWontAffectOtherAwContents() throws Throwable { 329 // Create another AwContents object. 330 final TestAwContentsClient awContentsClient = new TestAwContentsClient(); 331 final AwTestContainerView awTestContainerView = 332 mActivityTestRule.createAwTestContainerViewOnMainSync(awContentsClient); 333 final AwContents otherAwContents = awTestContainerView.getAwContents(); 334 AwActivityTestRule.enableJavaScriptOnUiThread(otherAwContents); 335 336 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 337 338 final String url = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML); 339 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url); 340 mActivityTestRule.loadUrlSync( 341 otherAwContents, awContentsClient.getOnPageFinishedHelper(), url); 342 343 // Verify that WebMessageListener was set successfually on mAwContents. 344 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 345 346 assertUrlHasOrigin(url, data.mSourceOrigin); 347 Assert.assertEquals(HELLO, data.mMessage); 348 Assert.assertEquals(0, data.mPorts.length); 349 350 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 351 352 // Verify that we don't have myObject injected to otherAwContents. 353 Assert.assertFalse(hasJavaScriptObject( 354 JS_OBJECT_NAME, mActivityTestRule, otherAwContents, awContentsClient)); 355 } 356 357 @Test 358 @SmallTest 359 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListenerAllowsCertainUrlWorksWithIframe()360 public void testAddWebMessageListenerAllowsCertainUrlWorksWithIframe() throws Throwable { 361 final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML); 362 final String html = createCrossOriginAccessTestPageHtml(frameUrl); 363 addWebMessageListenerOnUiThread( 364 mAwContents, JS_OBJECT_NAME, new String[] {parseOrigin(frameUrl)}, mListener); 365 366 mActivityTestRule.loadDataWithBaseUrlSync(mAwContents, 367 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false, 368 "http://www.google.com", null); 369 370 // The iframe should have myObject injected. 371 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 372 Assert.assertEquals(HELLO, data.mMessage); 373 374 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 375 376 // Verify that the main frame has no myObject injected. 377 Assert.assertFalse(hasJavaScriptObject( 378 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 379 } 380 381 @Test 382 @SmallTest 383 @Feature({"AndroidWebView", "JsJavaInteraction"}) testRemoveWebMessageListener_preventInjectionForNextPageLoad()384 public void testRemoveWebMessageListener_preventInjectionForNextPageLoad() throws Throwable { 385 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 386 387 // Load the the page. 388 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 389 390 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 391 Assert.assertEquals(HELLO, data.mMessage); 392 393 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 394 395 // Remove WebMessageListener will disable injection for next page load. 396 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 397 398 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 399 400 // Should have no myObject injected. 401 Assert.assertFalse(hasJavaScriptObject( 402 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 403 } 404 405 @Test 406 @SmallTest 407 @Feature({"AndroidWebView", "JsJavaInteraction"}) testRemoveWebMessageListener_cutJsJavaMappingImmediately()408 public void testRemoveWebMessageListener_cutJsJavaMappingImmediately() throws Throwable { 409 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 410 411 // Load the the page. 412 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 413 414 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 415 Assert.assertEquals(HELLO, data.mMessage); 416 417 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 418 419 // Remove WebMessageListener will disable injection for next page load. 420 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 421 422 // Should still have myObject. 423 Assert.assertTrue(hasJavaScriptObject( 424 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 425 426 // But posting message on myObject will be dropped. 427 mActivityTestRule.executeJavaScriptAndWaitForResult( 428 mAwContents, mContentsClient, JS_OBJECT_NAME + ".postMessage('" + HELLO + "');"); 429 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 430 } 431 432 @Test 433 @SmallTest 434 @Feature({"AndroidWebView", "JsJavaInteraction"}) testRemoveWebMessageListener_removeWithNoAddWebMessageListener()435 public void testRemoveWebMessageListener_removeWithNoAddWebMessageListener() throws Throwable { 436 // Call removeWebMessageListener() without addWebMessageListener() shouldn't fail. 437 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 438 439 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 440 } 441 442 @Test 443 @SmallTest 444 @Feature({"AndroidWebView", "JsJavaInteraction"}) testRemoveWebMessageListener_removeBeforeLoadPage()445 public void testRemoveWebMessageListener_removeBeforeLoadPage() throws Throwable { 446 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 447 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 448 449 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 450 451 Assert.assertFalse(hasJavaScriptObject( 452 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 453 } 454 455 @Test 456 @SmallTest 457 @Feature({"AndroidWebView", "JsJavaInteraction"}) testRemoveWebMessageListener_extraRemove()458 public void testRemoveWebMessageListener_extraRemove() throws Throwable { 459 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 460 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 461 // Extra removeWebMessageListener() does nothing. 462 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME); 463 464 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 465 466 Assert.assertFalse(hasJavaScriptObject( 467 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 468 } 469 470 @Test 471 @MediumTest 472 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAllowedOriginsWorksForVariousBaseUrls()473 public void testAllowedOriginsWorksForVariousBaseUrls() throws Throwable { 474 // Set a typical rule. 475 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, 476 new String[] {"https://www.example.com:443"}, mListener); 477 478 Assert.assertTrue( 479 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME)); 480 Assert.assertFalse( 481 isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080", JS_OBJECT_NAME)); 482 Assert.assertFalse( 483 isJsObjectInjectedWhenLoadingUrl("http://www.example.com", JS_OBJECT_NAME)); 484 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://example.com", JS_OBJECT_NAME)); 485 Assert.assertFalse( 486 isJsObjectInjectedWhenLoadingUrl("https://www.google.com", JS_OBJECT_NAME)); 487 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("file://etc", JS_OBJECT_NAME)); 488 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", JS_OBJECT_NAME)); 489 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl(null, JS_OBJECT_NAME)); 490 491 // Inject to all frames. 492 addWebMessageListenerOnUiThread( 493 mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, mListener); 494 495 Assert.assertTrue( 496 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2)); 497 Assert.assertTrue( 498 isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080", JS_OBJECT_NAME_2)); 499 Assert.assertTrue( 500 isJsObjectInjectedWhenLoadingUrl("http://www.example.com", JS_OBJECT_NAME_2)); 501 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("http://example.com", JS_OBJECT_NAME_2)); 502 Assert.assertTrue( 503 isJsObjectInjectedWhenLoadingUrl("https://www.google.com", JS_OBJECT_NAME_2)); 504 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc", JS_OBJECT_NAME_2)); 505 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", JS_OBJECT_NAME_2)); 506 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl(null, JS_OBJECT_NAME_2)); 507 508 // WebView doesn't support ftp with loadUrl() but ftp scheme could happen with 509 // loadDataWithBaseUrl(). 510 final String jsObjectName3 = JS_OBJECT_NAME + "3"; 511 addWebMessageListenerOnUiThread( 512 mAwContents, jsObjectName3, new String[] {"ftp://"}, mListener); 513 // ftp is a standard scheme, so the origin will be "ftp://example.com", however we don't 514 // support host rule for ftp://, so it won't do the injection. 515 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", jsObjectName3)); 516 517 // file scheme. 518 final String jsObjectName4 = JS_OBJECT_NAME + "4"; 519 addWebMessageListenerOnUiThread( 520 mAwContents, jsObjectName4, new String[] {"file://"}, mListener); 521 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc", jsObjectName4)); 522 523 // Pass an URI instead of origin shouldn't work. 524 final String jsObjectName5 = JS_OBJECT_NAME + "5"; 525 try { 526 addWebMessageListenerOnUiThread(mAwContents, jsObjectName5, 527 new String[] {"https://www.example.com/index.html"}, mListener); 528 Assert.fail("allowedOriginRules shouldn't be url like"); 529 } catch (RuntimeException e) { 530 // Should catch IllegalArgumentException in the end of the re-throw chain. 531 Assert.assertTrue(getRootCauseException(e) instanceof IllegalArgumentException); 532 } 533 Assert.assertFalse( 534 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", jsObjectName5)); 535 } 536 537 @Test 538 @MediumTest 539 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDontAllowAddWebMessageLitenerWithTheSameJsObjectName()540 public void testDontAllowAddWebMessageLitenerWithTheSameJsObjectName() { 541 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 542 try { 543 addWebMessageListenerOnUiThread( 544 mAwContents, JS_OBJECT_NAME, new String[] {"*"}, new TestWebMessageListener()); 545 Assert.fail("Shouldn't allow the same Js object name be added more than once."); 546 } catch (RuntimeException e) { 547 // Should catch IllegalArgumentException in the end of the re-throw chain. 548 Assert.assertTrue(getRootCauseException(e) instanceof IllegalArgumentException); 549 } 550 } 551 552 @Test 553 @MediumTest 554 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListener_SameOrigins()555 public void testAddWebMessageListener_SameOrigins() throws Throwable { 556 final String[] allowedOriginRules = 557 new String[] {"https://www.example.com", "https://www.allowed.com"}; 558 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, allowedOriginRules, mListener); 559 560 Assert.assertTrue( 561 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME)); 562 Assert.assertTrue( 563 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME)); 564 Assert.assertFalse( 565 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME)); 566 567 // Call addWebMessageListener() with the same set of origins. 568 addWebMessageListenerOnUiThread( 569 mAwContents, JS_OBJECT_NAME_2, allowedOriginRules, mListener); 570 571 Assert.assertTrue( 572 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2)); 573 Assert.assertTrue( 574 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2)); 575 Assert.assertFalse( 576 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2)); 577 } 578 579 @Test 580 @MediumTest 581 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListener_OverlappingSetOfOrigins()582 public void testAddWebMessageListener_OverlappingSetOfOrigins() throws Throwable { 583 final String[] allowedOriginRules1 = 584 new String[] {"https://www.example.com", "https://www.allowed.com"}; 585 addWebMessageListenerOnUiThread( 586 mAwContents, JS_OBJECT_NAME, allowedOriginRules1, mListener); 587 588 Assert.assertTrue( 589 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME)); 590 Assert.assertTrue( 591 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME)); 592 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME)); 593 Assert.assertFalse( 594 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME)); 595 596 final String[] allowedOriginRules2 = 597 new String[] {"https://www.example.com", "https://www.ok.com"}; 598 // Call addWebMessageListener with overlapping set of origins. 599 addWebMessageListenerOnUiThread( 600 mAwContents, JS_OBJECT_NAME_2, allowedOriginRules2, mListener); 601 602 Assert.assertTrue( 603 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2)); 604 Assert.assertFalse( 605 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2)); 606 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2)); 607 Assert.assertFalse( 608 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2)); 609 610 // Remove the listener should remove the js object from the next navigation. 611 removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME_2); 612 613 Assert.assertFalse( 614 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2)); 615 Assert.assertFalse( 616 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2)); 617 Assert.assertFalse( 618 isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2)); 619 Assert.assertFalse( 620 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2)); 621 } 622 623 @Test 624 @MediumTest 625 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListener_dontInjectWhenMatchesImplicitRules()626 public void testAddWebMessageListener_dontInjectWhenMatchesImplicitRules() throws Throwable { 627 // allowedOriginRules is an empty String array, shouldn't inject the object to any frame. 628 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[0], mListener); 629 630 // following are some origins allowed by implicit rules. 631 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://127.0.0.1", JS_OBJECT_NAME)); 632 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://127.0.0.1", JS_OBJECT_NAME)); 633 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://localhost", JS_OBJECT_NAME)); 634 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://169.254.0.1", JS_OBJECT_NAME)); 635 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://localhost6", JS_OBJECT_NAME)); 636 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://[::1]", JS_OBJECT_NAME)); 637 } 638 639 @Test 640 @MediumTest 641 @Feature({"AndroidWebView", "JsJavaInteraction"}) testAddWebMessageListener_NonOverlappingSetOfOrigins()642 public void testAddWebMessageListener_NonOverlappingSetOfOrigins() throws Throwable { 643 final String[] allowedOriginRules1 = 644 new String[] {"https://www.example.com", "https://www.allowed.com"}; 645 addWebMessageListenerOnUiThread( 646 mAwContents, JS_OBJECT_NAME, allowedOriginRules1, mListener); 647 648 Assert.assertTrue( 649 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME)); 650 Assert.assertTrue( 651 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME)); 652 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME)); 653 Assert.assertFalse( 654 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME)); 655 656 final String[] allowedOriginRules2 = new String[] {"https://www.ok.com"}; 657 // Call addWebMessageListener with non-overlapping set of origins. 658 addWebMessageListenerOnUiThread( 659 mAwContents, JS_OBJECT_NAME_2, allowedOriginRules2, mListener); 660 661 Assert.assertFalse( 662 isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2)); 663 Assert.assertFalse( 664 isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2)); 665 Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2)); 666 Assert.assertFalse( 667 isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2)); 668 Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("", JS_OBJECT_NAME_2)); 669 } 670 671 @Test 672 @MediumTest 673 @Feature({"AndroidWebView", "JsJavaInteraction"}) testJsReplyProxyWorks()674 public void testJsReplyProxyWorks() throws Throwable { 675 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 676 677 final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML); 678 679 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 680 681 // JavaScript code in the page will change the title to NEW_TITLE if postMessage to proxy 682 // succeed. 683 final OnReceivedTitleHelper onReceivedTitleHelper = 684 mContentsClient.getOnReceivedTitleHelper(); 685 final int titleCallCount = onReceivedTitleHelper.getCallCount(); 686 data.mReplyProxy.postMessage(NEW_TITLE); 687 onReceivedTitleHelper.waitForCallback(titleCallCount); 688 689 Assert.assertEquals(NEW_TITLE, onReceivedTitleHelper.getTitle()); 690 691 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 692 } 693 694 @Test 695 @MediumTest 696 @Feature({"AndroidWebView", "JsJavaInteraction"}) testJsReplyProxyReplyToTheCorrectJsObject()697 public void testJsReplyProxyReplyToTheCorrectJsObject() throws Throwable { 698 final TestWebMessageListener webMessageListener2 = new TestWebMessageListener(); 699 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 700 addWebMessageListenerOnUiThread( 701 mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, webMessageListener2); 702 703 final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML); 704 705 // Listener for myObject. 706 final String listener1 = "function (event) {" 707 + " " + JS_OBJECT_NAME + ".postMessage('ack1' + event.data);" 708 + "}"; 709 710 // Listener for myObject2. 711 final String listener2 = "function (event) {" 712 + " " + JS_OBJECT_NAME_2 + ".postMessage('ack2' + event.data);" 713 + "}"; 714 715 // Add two different js objects. 716 addEventListener(listener1, "listener1", JS_OBJECT_NAME, mActivityTestRule, mAwContents, 717 mContentsClient); 718 addEventListener(listener2, "listener2", JS_OBJECT_NAME_2, mActivityTestRule, mAwContents, 719 mContentsClient); 720 721 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 722 723 final String message = "message"; 724 mActivityTestRule.executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, 725 JS_OBJECT_NAME_2 + ".postMessage('" + message + "');"); 726 TestWebMessageListener.Data data2 = webMessageListener2.waitForOnPostMessage(); 727 728 Assert.assertEquals(message, data2.mMessage); 729 730 // Targeting myObject. 731 data.mReplyProxy.postMessage(HELLO); 732 // Targeting myObject2. 733 data2.mReplyProxy.postMessage(HELLO); 734 735 TestWebMessageListener.Data replyData1 = mListener.waitForOnPostMessage(); 736 TestWebMessageListener.Data replyData2 = webMessageListener2.waitForOnPostMessage(); 737 738 Assert.assertEquals("ack1" + HELLO, replyData1.mMessage); 739 Assert.assertEquals("ack2" + HELLO, replyData2.mMessage); 740 741 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 742 Assert.assertTrue(webMessageListener2.hasNoMoreOnPostMessage()); 743 } 744 745 @Test 746 @MediumTest 747 @Feature({"AndroidWebView", "JsJavaInteraction"}) testJsReplyProxyDropsMessageIfJsObjectIsGone()748 public void testJsReplyProxyDropsMessageIfJsObjectIsGone() throws Throwable { 749 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 750 751 final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML); 752 753 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 754 755 JsReplyProxy proxy = data.mReplyProxy; 756 757 // Load the same url again. 758 loadUrlFromPath(POST_MESSAGE_REPLY_HTML); 759 TestWebMessageListener.Data data2 = mListener.waitForOnPostMessage(); 760 761 // Use the previous JsReplyProxy to send message. It should drop the message. 762 proxy.postMessage(NEW_TITLE); 763 764 // Call evaluateJavascript to make sure the previous postMessage() call is reached to 765 // renderer if it should, since these messages are in sequence. 766 Assert.assertTrue(hasJavaScriptObject( 767 JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient)); 768 769 // Title shouldn't change. 770 Assert.assertNotEquals(NEW_TITLE, mActivityTestRule.getTitleOnUiThread(mAwContents)); 771 772 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 773 } 774 775 @Test 776 @MediumTest 777 @Feature({"AndroidWebView", "JsJavaInteraction"}) testJsAddAndRemoveEventListener()778 public void testJsAddAndRemoveEventListener() throws Throwable { 779 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 780 loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 781 782 JsReplyProxy proxy = mListener.waitForOnPostMessage().mReplyProxy; 783 784 final String listener1 = "function (event) {" 785 + " if (window.receivedCount1) {" 786 + " window.receivedCount1++;" 787 + " } else {" 788 + " window.receivedCount1 = 1;" 789 + " }" 790 + " " + JS_OBJECT_NAME + ".postMessage('ack1:' + window.receivedCount1);" 791 + "}"; 792 793 final String listener2 = "function (event) {" 794 + " if (window.receivedCount2) {" 795 + " window.receivedCount2++;" 796 + " } else {" 797 + " window.receivedCount2 = 1;" 798 + " }" 799 + " " + JS_OBJECT_NAME + ".postMessage('ack2:' + window.receivedCount2);" 800 + "}"; 801 802 addEventListener(listener1, "listener1", JS_OBJECT_NAME, mActivityTestRule, mAwContents, 803 mContentsClient); 804 addEventListener(listener2, "listener2", JS_OBJECT_NAME, mActivityTestRule, mAwContents, 805 mContentsClient); 806 807 // Post message to test both listeners receive message. 808 proxy.postMessage(HELLO); 809 810 TestWebMessageListener.Data replyData1 = mListener.waitForOnPostMessage(); 811 TestWebMessageListener.Data replyData2 = mListener.waitForOnPostMessage(); 812 813 Assert.assertEquals("ack1:1", replyData1.mMessage); 814 Assert.assertEquals("ack2:1", replyData2.mMessage); 815 816 removeEventListener( 817 "listener2", JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient); 818 819 // Post message again to test if remove works. 820 proxy.postMessage(HELLO); 821 822 // listener 1 should add message again. 823 TestWebMessageListener.Data replyData3 = mListener.waitForOnPostMessage(); 824 Assert.assertEquals("ack1:2", replyData3.mMessage); 825 826 // Should be no more messages. 827 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 828 } 829 830 @Test 831 @MediumTest 832 @Feature({"AndroidWebView", "JsJavaInteraction"}) testJsObjectRemoveOnMessage()833 public void testJsObjectRemoveOnMessage() throws Throwable { 834 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 835 836 final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML); 837 838 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 839 840 // JavaScript code in the page will change the title to NEW_TITLE if postMessage to proxy 841 // succeed. 842 final OnReceivedTitleHelper onReceivedTitleHelper = 843 mContentsClient.getOnReceivedTitleHelper(); 844 final int titleCallCount = onReceivedTitleHelper.getCallCount(); 845 data.mReplyProxy.postMessage(NEW_TITLE); 846 onReceivedTitleHelper.waitForCallback(titleCallCount); 847 848 Assert.assertEquals(NEW_TITLE, onReceivedTitleHelper.getTitle()); 849 850 mActivityTestRule.executeJavaScriptAndWaitForResult( 851 mAwContents, mContentsClient, JS_OBJECT_NAME + ".onmessage = undefined;"); 852 Assert.assertEquals("null", 853 mActivityTestRule.executeJavaScriptAndWaitForResult( 854 mAwContents, mContentsClient, JS_OBJECT_NAME + ".onmessage")); 855 856 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 857 } 858 verifyOnPostMessageOriginIsNull()859 private void verifyOnPostMessageOriginIsNull() throws Throwable { 860 mActivityTestRule.executeJavaScriptAndWaitForResult( 861 mAwContents, mContentsClient, JS_OBJECT_NAME + ".postMessage('Hello');"); 862 863 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 864 865 Assert.assertEquals("null", data.mSourceOrigin.toString()); 866 867 Assert.assertEquals(HELLO, data.mMessage); 868 Assert.assertTrue(data.mIsMainFrame); 869 Assert.assertEquals(0, data.mPorts.length); 870 871 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 872 } 873 874 @Test 875 @MediumTest 876 @Feature({"AndroidWebView", "JsJavaInteraction"}) testFileSchemeUrl_setAllowFileAccessFromFile_true()877 public void testFileSchemeUrl_setAllowFileAccessFromFile_true() throws Throwable { 878 mAwContents.getSettings().setAllowFileAccessFromFileURLs(true); 879 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 880 mActivityTestRule.loadUrlSync( 881 mAwContents, mContentsClient.getOnPageFinishedHelper(), FILE_URI); 882 Assert.assertEquals("\"file://\"", 883 mActivityTestRule.executeJavaScriptAndWaitForResult( 884 mAwContents, mContentsClient, "window.origin")); 885 886 verifyOnPostMessageOriginIsNull(); 887 } 888 889 @Test 890 @MediumTest 891 @Feature({"AndroidWebView", "JsJavaInteraction"}) testFileSchemeUrl_setAllowFileAccessFromFile_false()892 public void testFileSchemeUrl_setAllowFileAccessFromFile_false() throws Throwable { 893 // The default value is false on JELLY_BEAN and above, but we explicitly set this to 894 // false to readability. 895 mAwContents.getSettings().setAllowFileAccessFromFileURLs(false); 896 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 897 mActivityTestRule.loadUrlSync( 898 mAwContents, mContentsClient.getOnPageFinishedHelper(), FILE_URI); 899 Assert.assertEquals("\"null\"", 900 mActivityTestRule.executeJavaScriptAndWaitForResult( 901 mAwContents, mContentsClient, "window.origin")); 902 903 verifyOnPostMessageOriginIsNull(); 904 } 905 906 @Test 907 @MediumTest 908 @Feature({"AndroidWebView", "JsJavaInteraction"}) testContentSchemeUrl_setAllowFileAccessFromFileURLs_true()909 public void testContentSchemeUrl_setAllowFileAccessFromFileURLs_true() throws Throwable { 910 mAwContents.getSettings().setAllowContentAccess(true); 911 mAwContents.getSettings().setAllowFileAccessFromFileURLs(true); 912 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 913 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), 914 TestContentProvider.createContentUrl("content_access")); 915 Assert.assertEquals("\"content://\"", 916 mActivityTestRule.executeJavaScriptAndWaitForResult( 917 mAwContents, mContentsClient, "window.origin")); 918 919 verifyOnPostMessageOriginIsNull(); 920 } 921 922 @Test 923 @MediumTest 924 @Feature({"AndroidWebView", "JsJavaInteraction"}) testContentSchemeUrl_setAllowFileAccessFromFileURLs_false()925 public void testContentSchemeUrl_setAllowFileAccessFromFileURLs_false() throws Throwable { 926 mAwContents.getSettings().setAllowContentAccess(true); 927 // The default value is false on JELLY_BEAN and above, but we explicitly set this to 928 // false to readability. 929 mAwContents.getSettings().setAllowFileAccessFromFileURLs(false); 930 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 931 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), 932 TestContentProvider.createContentUrl("content_access")); 933 Assert.assertEquals("\"null\"", 934 mActivityTestRule.executeJavaScriptAndWaitForResult( 935 mAwContents, mContentsClient, "window.origin")); 936 937 verifyOnPostMessageOriginIsNull(); 938 } 939 940 @Test 941 @SmallTest 942 @Feature({"AndroidWebView"}) testWebMessageListenerForPopupWindow()943 public void testWebMessageListenerForPopupWindow() throws Throwable { 944 TestWebServer webServer = TestWebServer.start(); 945 946 final String popupPath = "/popup.html"; 947 final String parentPageHtml = CommonResources.makeHtmlPageFrom("", 948 "<script>" 949 + "function tryOpenWindow() {" 950 + " var newWindow = window.open('" + popupPath + "');" 951 + "}</script>"); 952 953 final String popupPageHtml = 954 CommonResources.makeHtmlPageFrom("<title>popup</title>", "This is a popup window"); 955 956 mActivityTestRule.triggerPopup(mAwContents, mContentsClient, webServer, parentPageHtml, 957 popupPageHtml, popupPath, "tryOpenWindow()"); 958 AwActivityTestRule.PopupInfo popupInfo = mActivityTestRule.createPopupContents(mAwContents); 959 TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient; 960 final AwContents popupContents = popupInfo.popupContents; 961 962 // App adds WebMessageListener to the child WebView, e.g. in onCreateWindow(). 963 addWebMessageListenerOnUiThread( 964 popupContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 965 966 mActivityTestRule.loadPopupContents(mAwContents, popupInfo, null); 967 968 // Test if the listener was re-injected. 969 mActivityTestRule.executeJavaScriptAndWaitForResult( 970 popupContents, popupContentsClient, JS_OBJECT_NAME + ".postMessage('Hello');"); 971 972 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 973 974 Assert.assertEquals(HELLO, data.mMessage); 975 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 976 977 webServer.shutdown(); 978 } 979 980 @Test 981 @SmallTest 982 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessage_JsObjectName_Number()983 public void testPostMessage_JsObjectName_Number() throws Throwable { 984 checkInjectAndAccessJsObjectNameAsWindowProperty("123"); 985 } 986 987 @Test 988 @SmallTest 989 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessage_JsObjectName_Symbol()990 public void testPostMessage_JsObjectName_Symbol() throws Throwable { 991 checkInjectAndAccessJsObjectNameAsWindowProperty("*"); 992 } 993 994 @Test 995 @SmallTest 996 @Feature({"AndroidWebView", "JsJavaInteraction"}) testPostMessage_JsObjectName_Keyword()997 public void testPostMessage_JsObjectName_Keyword() throws Throwable { 998 checkInjectAndAccessJsObjectNameAsWindowProperty("var"); 999 } 1000 1001 @Test 1002 @MediumTest 1003 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_addJavascriptInterfaceShouldBeAvaliable()1004 public void testDocumentStartJavaScript_addJavascriptInterfaceShouldBeAvaliable() 1005 throws Throwable { 1006 final LinkedBlockingQueue<String> javascriptInterfaceQueue = new LinkedBlockingQueue<>(); 1007 AwActivityTestRule.addJavascriptInterfaceOnUiThread(mAwContents, new Object() { 1008 @JavascriptInterface 1009 public void send(String message) { 1010 javascriptInterfaceQueue.add(message); 1011 } 1012 }, "javaBridge"); 1013 addDocumentStartJavaScriptOnUiThread( 1014 mAwContents, "javaBridge.send('" + HELLO + "');", new String[] {"*"}); 1015 1016 loadUrlFromPath(HELLO_WORLD_HTML); 1017 1018 String message = AwActivityTestRule.waitForNextQueueElement(javascriptInterfaceQueue); 1019 1020 Assert.assertEquals(HELLO, message); 1021 } 1022 1023 @Test 1024 @MediumTest 1025 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_jsObjectShouldBeAvaliable()1026 public void testDocumentStartJavaScript_jsObjectShouldBeAvaliable() throws Throwable { 1027 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1028 addDocumentStartJavaScriptOnUiThread( 1029 mAwContents, JS_OBJECT_NAME + ".postMessage('" + HELLO + "');", new String[] {"*"}); 1030 1031 String url = loadUrlFromPath(HELLO_WORLD_HTML); 1032 1033 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1034 1035 assertUrlHasOrigin(url, data.mSourceOrigin); 1036 Assert.assertEquals(HELLO, data.mMessage); 1037 Assert.assertTrue(data.mIsMainFrame); 1038 Assert.assertEquals(0, data.mPorts.length); 1039 1040 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1041 } 1042 1043 @Test 1044 @MediumTest 1045 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_runBeforeUserScript()1046 public void testDocumentStartJavaScript_runBeforeUserScript() throws Throwable { 1047 addDocumentStartJavaScriptOnUiThread(mAwContents, 1048 JS_OBJECT_NAME + ".postMessage('" + HELLO + "1');", new String[] {"*"}); 1049 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1050 1051 // POST_MESSAGE_SIMPLE_HTML will post HELLO message. 1052 String url = loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML); 1053 1054 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1055 1056 assertUrlHasOrigin(url, data.mSourceOrigin); 1057 Assert.assertEquals(HELLO + "1", data.mMessage); 1058 Assert.assertTrue(data.mIsMainFrame); 1059 Assert.assertEquals(0, data.mPorts.length); 1060 1061 TestWebMessageListener.Data data2 = mListener.waitForOnPostMessage(); 1062 Assert.assertEquals(HELLO, data2.mMessage); 1063 Assert.assertTrue(data2.mIsMainFrame); 1064 1065 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1066 } 1067 1068 @Test 1069 @MediumTest 1070 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_multipleScripts()1071 public void testDocumentStartJavaScript_multipleScripts() throws Throwable { 1072 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1073 addDocumentStartJavaScriptOnUiThread(mAwContents, 1074 JS_OBJECT_NAME + ".postMessage('" + HELLO + "0');", new String[] {"*"}); 1075 addDocumentStartJavaScriptOnUiThread(mAwContents, 1076 JS_OBJECT_NAME + ".postMessage('" + HELLO + "1');", new String[] {"*"}); 1077 1078 String url = loadUrlFromPath(HELLO_WORLD_HTML); 1079 1080 for (int i = 0; i < 2; ++i) { 1081 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1082 1083 assertUrlHasOrigin(url, data.mSourceOrigin); 1084 Assert.assertEquals(HELLO + Integer.toString(i), data.mMessage); 1085 Assert.assertTrue(data.mIsMainFrame); 1086 Assert.assertEquals(0, data.mPorts.length); 1087 } 1088 1089 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1090 } 1091 1092 @Test 1093 @MediumTest 1094 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_callAgainAfterPageLoad()1095 public void testDocumentStartJavaScript_callAgainAfterPageLoad() throws Throwable { 1096 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1097 addDocumentStartJavaScriptOnUiThread(mAwContents, 1098 JS_OBJECT_NAME + ".postMessage('" + HELLO + "0');", new String[] {"*"}); 1099 1100 String url = loadUrlFromPath(HELLO_WORLD_HTML); 1101 1102 addDocumentStartJavaScriptOnUiThread(mAwContents, 1103 JS_OBJECT_NAME + ".postMessage('" + HELLO + "1');", new String[] {"*"}); 1104 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1105 1106 assertUrlHasOrigin(url, data.mSourceOrigin); 1107 Assert.assertEquals(HELLO + "0", data.mMessage); 1108 Assert.assertTrue(data.mIsMainFrame); 1109 Assert.assertEquals(0, data.mPorts.length); 1110 1111 // Load the page again. 1112 loadUrlFromPath(HELLO_WORLD_HTML); 1113 1114 for (int i = 0; i < 2; ++i) { 1115 TestWebMessageListener.Data data2 = mListener.waitForOnPostMessage(); 1116 1117 Assert.assertEquals(HELLO + Integer.toString(i), data2.mMessage); 1118 Assert.assertTrue(data2.mIsMainFrame); 1119 } 1120 1121 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1122 } 1123 1124 @Test 1125 @MediumTest 1126 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_allowedOriginsRulesWithDifferentBaseUrl()1127 public void testDocumentStartJavaScript_allowedOriginsRulesWithDifferentBaseUrl() 1128 throws Throwable { 1129 // With a standard origin rule. 1130 final String testObjectName = "test"; 1131 addDocumentStartJavaScriptOnUiThread(mAwContents, "let " + testObjectName + " = {};", 1132 new String[] {"https://www.example.com:443"}); 1133 1134 Assert.assertTrue(didScriptRunWhenLoading("https://www.example.com", testObjectName)); 1135 Assert.assertFalse(didScriptRunWhenLoading("https://www.example.com:8080", testObjectName)); 1136 Assert.assertFalse(didScriptRunWhenLoading("http://www.example.com", testObjectName)); 1137 Assert.assertFalse(didScriptRunWhenLoading("http://example.com", testObjectName)); 1138 Assert.assertFalse(didScriptRunWhenLoading("https://www.google.com", testObjectName)); 1139 Assert.assertFalse(didScriptRunWhenLoading("file://etc", testObjectName)); 1140 Assert.assertFalse(didScriptRunWhenLoading("ftp://example.com", testObjectName)); 1141 Assert.assertFalse(didScriptRunWhenLoading(null, testObjectName)); 1142 1143 // Match all the origins. 1144 final String testObjectName2 = testObjectName + "2"; 1145 addDocumentStartJavaScriptOnUiThread( 1146 mAwContents, "let " + testObjectName2 + " = {};", new String[] {"*"}); 1147 1148 Assert.assertTrue(didScriptRunWhenLoading("https://www.example.com", testObjectName2)); 1149 Assert.assertTrue(didScriptRunWhenLoading("https://www.example.com:8080", testObjectName2)); 1150 Assert.assertTrue(didScriptRunWhenLoading("http://www.example.com", testObjectName2)); 1151 Assert.assertTrue(didScriptRunWhenLoading("http://example.com", testObjectName2)); 1152 Assert.assertTrue(didScriptRunWhenLoading("https://www.google.com", testObjectName2)); 1153 Assert.assertTrue(didScriptRunWhenLoading("file://etc", testObjectName2)); 1154 Assert.assertTrue(didScriptRunWhenLoading("ftp://example.com", testObjectName2)); 1155 Assert.assertTrue(didScriptRunWhenLoading(null, testObjectName2)); 1156 // data: scheme could be matched with "*". 1157 final String html = "<html><body><div>data</div></body></html>"; 1158 mActivityTestRule.loadHtmlSync( 1159 mAwContents, mContentsClient.getOnPageFinishedHelper(), html); 1160 Assert.assertTrue(hasJavaScriptObject( 1161 testObjectName2, mActivityTestRule, mAwContents, mContentsClient)); 1162 1163 // Wrong origin rule. 1164 final String testObjectName5 = testObjectName + "5"; 1165 try { 1166 addDocumentStartJavaScriptOnUiThread(mAwContents, "let " + testObjectName5 + " = {};", 1167 new String[] {"https://www.example.com/index.html"}); 1168 Assert.fail("You cannot use a full URL for allowedOriginRules."); 1169 } catch (RuntimeException e) { 1170 // Should catch IllegalArgumentException in the end of the re-throw chain. 1171 Assert.assertTrue("The exception should be an IllegalArgumentException", 1172 getRootCauseException(e) instanceof IllegalArgumentException); 1173 } 1174 Assert.assertFalse(didScriptRunWhenLoading("https://www.example.com", testObjectName5)); 1175 } 1176 1177 @Test 1178 @MediumTest 1179 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_willRunInIframe()1180 public void testDocumentStartJavaScript_willRunInIframe() throws Throwable { 1181 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1182 final String script = "if (window.location.origin !== 'http://www.google.com') {" 1183 + " " + JS_OBJECT_NAME + ".postMessage('" + HELLO + "');" 1184 + "}"; 1185 // Since we are matching both origins, the script will run in both iframe and main frame, 1186 // but it will send message in only iframe. 1187 addDocumentStartJavaScriptOnUiThread(mAwContents, script, new String[] {"*"}); 1188 1189 final String frameUrl = mTestServer.getURL(HELLO_WORLD_HTML); 1190 final String html = createCrossOriginAccessTestPageHtml(frameUrl); 1191 1192 // Load a cross origin iframe page, the www.google.com page is the main frame, test server 1193 // page is the iframe. 1194 mActivityTestRule.loadDataWithBaseUrlSync(mAwContents, 1195 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false, 1196 "http://www.google.com", null); 1197 1198 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1199 1200 assertUrlHasOrigin(frameUrl, data.mSourceOrigin); 1201 Assert.assertEquals(HELLO, data.mMessage); 1202 Assert.assertFalse(data.mIsMainFrame); 1203 Assert.assertEquals(0, data.mPorts.length); 1204 1205 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1206 } 1207 1208 @Test 1209 @MediumTest 1210 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_removeScript()1211 public void testDocumentStartJavaScript_removeScript() throws Throwable { 1212 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1213 ScriptReference[] references = new ScriptReference[2]; 1214 for (int i = 0; i < 2; ++i) { 1215 final String script = 1216 JS_OBJECT_NAME + ".postMessage('" + HELLO + Integer.toString(i) + "');"; 1217 // Since we are matching both origins, the script will run in both iframe and main 1218 // frame, but it will send message in only iframe. 1219 references[i] = 1220 addDocumentStartJavaScriptOnUiThread(mAwContents, script, new String[] {"*"}); 1221 } 1222 1223 final String url = loadUrlFromPath(HELLO_WORLD_HTML); 1224 1225 for (int i = 0; i < 2; ++i) { 1226 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1227 1228 assertUrlHasOrigin(url, data.mSourceOrigin); 1229 Assert.assertEquals(HELLO + Integer.toString(i), data.mMessage); 1230 } 1231 1232 TestThreadUtils.runOnUiThreadBlocking(() -> references[0].remove()); 1233 // Load the page again. 1234 loadUrlFromPath(HELLO_WORLD_HTML); 1235 1236 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1237 Assert.assertEquals(HELLO + "1", data.mMessage); 1238 1239 TestThreadUtils.runOnUiThreadBlocking(() -> references[1].remove()); 1240 // Load the page again. 1241 loadUrlFromPath(HELLO_WORLD_HTML); 1242 1243 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1244 } 1245 1246 @Test 1247 @MediumTest 1248 @Feature({"AndroidWebView", "JsJavaInteraction"}) testDocumentStartJavaScript_doubleRemoveScript()1249 public void testDocumentStartJavaScript_doubleRemoveScript() throws Throwable { 1250 addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener); 1251 1252 final String script = JS_OBJECT_NAME + ".postMessage('" + HELLO + "');"; 1253 ScriptReference reference = 1254 addDocumentStartJavaScriptOnUiThread(mAwContents, script, new String[] {"*"}); 1255 1256 final String url = loadUrlFromPath(HELLO_WORLD_HTML); 1257 1258 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1259 1260 assertUrlHasOrigin(url, data.mSourceOrigin); 1261 Assert.assertEquals(HELLO, data.mMessage); 1262 1263 // Remove twice, the second time should take no effect. 1264 TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove()); 1265 TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove()); 1266 // Load the page again. 1267 loadUrlFromPath(HELLO_WORLD_HTML); 1268 1269 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1270 1271 // Remove twice again, should have no effect. 1272 TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove()); 1273 TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove()); 1274 // Load the page again. 1275 loadUrlFromPath(HELLO_WORLD_HTML); 1276 1277 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1278 } 1279 checkInjectAndAccessJsObjectNameAsWindowProperty(String jsObjName)1280 private void checkInjectAndAccessJsObjectNameAsWindowProperty(String jsObjName) 1281 throws Throwable { 1282 addWebMessageListenerOnUiThread(mAwContents, jsObjName, new String[] {"*"}, mListener); 1283 1284 String html = "<html><head><script>window['" + jsObjName + "'].postMessage('Hello');" 1285 + "</script></head><body><div>postMessage</div></body></html>"; 1286 mActivityTestRule.loadDataWithBaseUrlSync(mAwContents, 1287 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false, 1288 "http://www.google.com", null); 1289 1290 TestWebMessageListener.Data data = mListener.waitForOnPostMessage(); 1291 1292 Assert.assertEquals("http://www.google.com", data.mSourceOrigin.toString()); 1293 Assert.assertEquals(HELLO, data.mMessage); 1294 Assert.assertTrue(data.mIsMainFrame); 1295 Assert.assertEquals(0, data.mPorts.length); 1296 1297 Assert.assertTrue(mListener.hasNoMoreOnPostMessage()); 1298 } 1299 isJsObjectInjectedWhenLoadingUrl( final String baseUrl, final String jsObjectName)1300 private boolean isJsObjectInjectedWhenLoadingUrl( 1301 final String baseUrl, final String jsObjectName) throws Throwable { 1302 mActivityTestRule.loadDataWithBaseUrlSync(mAwContents, 1303 mContentsClient.getOnPageFinishedHelper(), DATA_HTML, "text/html", false, baseUrl, 1304 null); 1305 return hasJavaScriptObject(jsObjectName, mActivityTestRule, mAwContents, mContentsClient); 1306 } 1307 1308 // The script is trying to set a global JavaScript object, so it is essentially the same 1309 // with isJsObjectInjectedWhenLoadingUrl(). Having a wrapper method to make it clear for 1310 // the context. didScriptRunWhenLoading(final String baseUrl, final String objectName)1311 private boolean didScriptRunWhenLoading(final String baseUrl, final String objectName) 1312 throws Throwable { 1313 return isJsObjectInjectedWhenLoadingUrl(baseUrl, objectName); 1314 } 1315 loadUrlFromPath(String path)1316 private String loadUrlFromPath(String path) throws Exception { 1317 final String url = mTestServer.getURL(path); 1318 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url); 1319 return url; 1320 } 1321 assertUrlHasOrigin(final String url, final Uri origin)1322 private static void assertUrlHasOrigin(final String url, final Uri origin) { 1323 Uri uriFromServer = Uri.parse(url); 1324 Assert.assertEquals(uriFromServer.getScheme(), origin.getScheme()); 1325 Assert.assertEquals(uriFromServer.getHost(), origin.getHost()); 1326 Assert.assertEquals(uriFromServer.getPort(), origin.getPort()); 1327 } 1328 createCrossOriginAccessTestPageHtml(final String frameUrl)1329 private static String createCrossOriginAccessTestPageHtml(final String frameUrl) { 1330 return "<html>" 1331 + "<body><div>I have an iframe</ div>" 1332 + " <iframe src ='" + frameUrl + "'></iframe>" 1333 + "</body></html>"; 1334 } 1335 addDocumentStartJavaScriptOnUiThread( final AwContents awContents, final String script, final String[] allowedOriginRules)1336 private static ScriptReference addDocumentStartJavaScriptOnUiThread( 1337 final AwContents awContents, final String script, final String[] allowedOriginRules) { 1338 return TestThreadUtils.runOnUiThreadBlockingNoException( 1339 () -> awContents.addDocumentStartJavaScript(script, allowedOriginRules)); 1340 } 1341 addWebMessageListenerOnUiThread(final AwContents awContents, final String jsObjectName, final String[] allowedOriginRules, final WebMessageListener listener)1342 private static void addWebMessageListenerOnUiThread(final AwContents awContents, 1343 final String jsObjectName, final String[] allowedOriginRules, 1344 final WebMessageListener listener) { 1345 TestThreadUtils.runOnUiThreadBlocking( 1346 () -> awContents.addWebMessageListener(jsObjectName, allowedOriginRules, listener)); 1347 } 1348 removeWebMessageListenerOnUiThread( final AwContents awContents, final String jsObjectName)1349 private static void removeWebMessageListenerOnUiThread( 1350 final AwContents awContents, final String jsObjectName) { 1351 TestThreadUtils.runOnUiThreadBlocking( 1352 () -> awContents.removeWebMessageListener(jsObjectName)); 1353 } 1354 hasJavaScriptObject(final String jsObjectName, final AwActivityTestRule rule, final AwContents awContents, final TestAwContentsClient contentsClient)1355 private static boolean hasJavaScriptObject(final String jsObjectName, 1356 final AwActivityTestRule rule, final AwContents awContents, 1357 final TestAwContentsClient contentsClient) throws Throwable { 1358 final String result = rule.executeJavaScriptAndWaitForResult( 1359 awContents, contentsClient, "typeof " + jsObjectName + " !== 'undefined'"); 1360 return result.equals("true"); 1361 } 1362 addEventListener(final String func, final String funcName, String jsObjectName, final AwActivityTestRule rule, final AwContents awContents, final TestAwContentsClient contentsClient)1363 private static void addEventListener(final String func, final String funcName, 1364 String jsObjectName, final AwActivityTestRule rule, final AwContents awContents, 1365 final TestAwContentsClient contentsClient) throws Throwable { 1366 String code = "let " + funcName + " = " + func + ";"; 1367 code += jsObjectName + ".addEventListener('message', " + funcName + ");"; 1368 rule.executeJavaScriptAndWaitForResult(awContents, contentsClient, code); 1369 } 1370 removeEventListener(final String funcName, final String jsObjectName, final AwActivityTestRule rule, final AwContents awContents, final TestAwContentsClient contentsClient)1371 private static void removeEventListener(final String funcName, final String jsObjectName, 1372 final AwActivityTestRule rule, final AwContents awContents, 1373 final TestAwContentsClient contentsClient) throws Throwable { 1374 String code = jsObjectName + ".removeEventListener('message', " + funcName + ")"; 1375 rule.executeJavaScriptAndWaitForResult(awContents, contentsClient, code); 1376 } 1377 parseOrigin(String url)1378 private static String parseOrigin(String url) { 1379 final Uri uri = Uri.parse(url); 1380 final StringBuilder sb = new StringBuilder(); 1381 final String scheme = uri.getScheme(); 1382 final int port = uri.getPort(); 1383 1384 if (!scheme.isEmpty()) { 1385 sb.append(scheme); 1386 sb.append("://"); 1387 } 1388 sb.append(uri.getHost()); 1389 if (port != -1) { 1390 sb.append(":"); 1391 sb.append(port); 1392 } 1393 return sb.toString(); 1394 } 1395 getRootCauseException(Throwable exception)1396 private static Throwable getRootCauseException(Throwable exception) { 1397 while (exception.getCause() != null) { 1398 exception = exception.getCause(); 1399 } 1400 return exception; 1401 } 1402 } 1403