1 // Copyright 2012 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 static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; 8 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS; 9 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS; 10 11 import android.graphics.Bitmap; 12 import android.graphics.BitmapFactory; 13 import android.graphics.Canvas; 14 import android.graphics.Color; 15 import android.os.Handler; 16 import android.os.Looper; 17 import android.os.Message; 18 import android.support.test.InstrumentationRegistry; 19 import android.util.Pair; 20 import android.view.KeyEvent; 21 import android.view.View; 22 import android.webkit.JavascriptInterface; 23 24 import androidx.test.filters.LargeTest; 25 import androidx.test.filters.MediumTest; 26 import androidx.test.filters.SmallTest; 27 28 import com.google.common.collect.ImmutableMap; 29 30 import org.junit.Assert; 31 import org.junit.Rule; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 import org.chromium.android_webview.AwContents; 36 import org.chromium.android_webview.AwRenderProcess; 37 import org.chromium.android_webview.AwSettings; 38 import org.chromium.android_webview.renderer_priority.RendererPriority; 39 import org.chromium.android_webview.test.TestAwContentsClient.OnDownloadStartHelper; 40 import org.chromium.android_webview.test.util.CommonResources; 41 import org.chromium.base.BuildInfo; 42 import org.chromium.base.Log; 43 import org.chromium.base.metrics.RecordHistogram; 44 import org.chromium.base.test.util.CallbackHelper; 45 import org.chromium.base.test.util.CommandLineFlags; 46 import org.chromium.base.test.util.Feature; 47 import org.chromium.components.viz.common.VizFeatures; 48 import org.chromium.content_public.browser.test.util.TestThreadUtils; 49 import org.chromium.content_public.common.ContentSwitches; 50 import org.chromium.content_public.common.ContentUrlConstants; 51 import org.chromium.net.test.EmbeddedTestServer; 52 import org.chromium.net.test.util.TestWebServer; 53 54 import java.io.InputStream; 55 import java.net.URL; 56 import java.util.ArrayList; 57 import java.util.HashMap; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.concurrent.Semaphore; 61 import java.util.concurrent.TimeUnit; 62 import java.util.concurrent.TimeoutException; 63 import java.util.concurrent.atomic.AtomicInteger; 64 65 /** 66 * AwContents tests. 67 */ 68 @RunWith(AwJUnit4ClassRunner.class) 69 public class AwContentsTest { 70 private static final String TAG = "AwContentsTest"; 71 72 @Rule 73 public AwActivityTestRule mActivityTestRule = new AwActivityTestRule(); 74 75 private TestAwContentsClient mContentsClient = new TestAwContentsClient(); 76 private volatile Integer mHistogramTotalCount = 0; 77 78 @Test 79 @SmallTest 80 @Feature({"AndroidWebView"}) testCreateDestroy()81 public void testCreateDestroy() throws Throwable { 82 // NOTE this test runs on UI thread, so we cannot call any async methods. 83 mActivityTestRule.runOnUiThread(() -> mActivityTestRule.createAwTestContainerView( 84 mContentsClient) 85 .getAwContents() 86 .destroy()); 87 } 88 89 @Test 90 @SmallTest 91 @Feature({"AndroidWebView"}) testCreateLoadPageDestroy()92 public void testCreateLoadPageDestroy() throws Throwable { 93 AwTestContainerView awTestContainerView = 94 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 95 mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(), 96 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html", 97 false); 98 99 mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents()); 100 // It should be safe to call destroy multiple times. 101 mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents()); 102 } 103 104 @Test 105 @LargeTest 106 @Feature({"AndroidWebView"}) testCreateLoadDestroyManyTimes()107 public void testCreateLoadDestroyManyTimes() throws Throwable { 108 for (int i = 0; i < 10; ++i) { 109 AwTestContainerView testView = 110 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 111 AwContents awContents = testView.getAwContents(); 112 113 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 114 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 115 mActivityTestRule.destroyAwContentsOnMainSync(awContents); 116 } 117 } 118 119 @Test 120 @LargeTest 121 @Feature({"AndroidWebView"}) testCreateLoadDestroyManyAtOnce()122 public void testCreateLoadDestroyManyAtOnce() throws Throwable { 123 AwTestContainerView views[] = new AwTestContainerView[10]; 124 125 for (int i = 0; i < views.length; ++i) { 126 views[i] = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 127 mActivityTestRule.loadUrlSync(views[i].getAwContents(), 128 mContentsClient.getOnPageFinishedHelper(), 129 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 130 } 131 132 for (int i = 0; i < views.length; ++i) { 133 mActivityTestRule.destroyAwContentsOnMainSync(views[i].getAwContents()); 134 views[i] = null; 135 } 136 } 137 138 @Test 139 @SmallTest 140 @Feature({"AndroidWebView"}) testWebViewApisFailGracefullyAfterDestruction()141 public void testWebViewApisFailGracefullyAfterDestruction() throws Throwable { 142 mActivityTestRule.runOnUiThread(() -> { 143 AwContents awContents = mActivityTestRule.createAwTestContainerView(mContentsClient) 144 .getAwContents(); 145 awContents.destroy(); 146 147 // The documentation for WebView#destroy() reads "This method should be called 148 // after this WebView has been removed from the view system. No other methods 149 // may be called on this WebView after destroy". 150 // However, some apps do not respect that restriction so we need to ensure that 151 // we fail gracefully and do not crash when APIs are invoked after destruction. 152 // Due to the large number of APIs we only test a representative selection here. 153 awContents.clearHistory(); 154 Assert.assertNull(awContents.getOriginalUrl()); 155 Assert.assertNull(awContents.getNavigationHistory()); 156 awContents.loadUrl("http://www.google.com"); 157 awContents.findAllAsync("search"); 158 Assert.assertNull(awContents.getUrl()); 159 Assert.assertFalse(awContents.canGoBack()); 160 awContents.disableJavascriptInterfacesInspection(); 161 awContents.invokeZoomPicker(); 162 awContents.onResume(); 163 awContents.stopLoading(); 164 awContents.onWindowVisibilityChanged(View.VISIBLE); 165 awContents.requestFocus(); 166 awContents.isMultiTouchZoomSupported(); 167 awContents.setOverScrollMode(View.OVER_SCROLL_NEVER); 168 awContents.pauseTimers(); 169 awContents.onContainerViewScrollChanged(200, 200, 100, 100); 170 awContents.computeScroll(); 171 awContents.onMeasure(100, 100); 172 awContents.onDraw(new Canvas()); 173 awContents.getMostRecentProgress(); 174 Assert.assertEquals(0, awContents.computeHorizontalScrollOffset()); 175 Assert.assertEquals(0, awContents.getContentWidthCss()); 176 awContents.onKeyUp(KeyEvent.KEYCODE_BACK, 177 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); 178 }); 179 } 180 181 @Test 182 @SmallTest 183 @Feature({"AndroidWebView"}) testUseAwSettingsAfterDestroy()184 public void testUseAwSettingsAfterDestroy() throws Throwable { 185 AwTestContainerView awTestContainerView = 186 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 187 AwSettings awSettings = 188 mActivityTestRule.getAwSettingsOnUiThread(awTestContainerView.getAwContents()); 189 mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(), 190 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html", 191 false); 192 mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents()); 193 194 // AwSettings should still be usable even after native side is destroyed. 195 String newFontFamily = "serif"; 196 awSettings.setStandardFontFamily(newFontFamily); 197 Assert.assertEquals(newFontFamily, awSettings.getStandardFontFamily()); 198 boolean newBlockNetworkLoads = !awSettings.getBlockNetworkLoads(); 199 awSettings.setBlockNetworkLoads(newBlockNetworkLoads); 200 Assert.assertEquals(newBlockNetworkLoads, awSettings.getBlockNetworkLoads()); 201 } 202 203 @Test 204 @SmallTest 205 @Feature({"AndroidWebView"}) testBackgroundColorInDarkMode()206 public void testBackgroundColorInDarkMode() throws Throwable { 207 mActivityTestRule.runOnUiThread(() -> { 208 AwContents awContents = 209 mActivityTestRule.createAwTestContainerView(mContentsClient).getAwContents(); 210 AwSettings awSettings = awContents.getSettings(); 211 212 Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.WHITE); 213 214 awSettings.setForceDarkMode(AwSettings.FORCE_DARK_ON); 215 Assert.assertTrue(awSettings.isDarkMode()); 216 Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.BLACK); 217 218 awContents.setBackgroundColor(Color.RED); 219 Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.RED); 220 221 awContents.destroy(); 222 Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.RED); 223 }); 224 } 225 callDocumentHasImagesSync(final AwContents awContents)226 private int callDocumentHasImagesSync(final AwContents awContents) 227 throws Throwable, InterruptedException { 228 // Set up a container to hold the result object and a semaphore to 229 // make the test wait for the result. 230 final AtomicInteger val = new AtomicInteger(); 231 final Semaphore s = new Semaphore(0); 232 final Message msg = Message.obtain(new Handler(Looper.getMainLooper()) { 233 @Override 234 public void handleMessage(Message msg) { 235 val.set(msg.arg1); 236 s.release(); 237 } 238 }); 239 InstrumentationRegistry.getInstrumentation().runOnMainSync( 240 () -> awContents.documentHasImages(msg)); 241 Assert.assertTrue(s.tryAcquire(AwActivityTestRule.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 242 int result = val.get(); 243 return result; 244 } 245 246 @Test 247 @SmallTest 248 @Feature({"AndroidWebView"}) testDocumentHasImages()249 public void testDocumentHasImages() throws Throwable { 250 AwTestContainerView testView = 251 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 252 AwContents awContents = testView.getAwContents(); 253 254 final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper(); 255 256 final String mime = "text/html"; 257 final String emptyDoc = "<head/><body/>"; 258 final String imageDoc = "<head/><body><img/><img/></body>"; 259 260 // Make sure a document that does not have images returns 0 261 mActivityTestRule.loadDataSync(awContents, loadHelper, emptyDoc, mime, false); 262 int result = callDocumentHasImagesSync(awContents); 263 Assert.assertEquals(0, result); 264 265 // Make sure a document that does have images returns 1 266 mActivityTestRule.loadDataSync(awContents, loadHelper, imageDoc, mime, false); 267 result = callDocumentHasImagesSync(awContents); 268 Assert.assertEquals(1, result); 269 } 270 271 @Test 272 @SmallTest 273 @Feature({"AndroidWebView"}) testClearCacheMemoryAndDisk()274 public void testClearCacheMemoryAndDisk() throws Throwable { 275 final AwTestContainerView testContainer = 276 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 277 final AwContents awContents = testContainer.getAwContents(); 278 279 TestWebServer webServer = TestWebServer.start(); 280 try { 281 final String pagePath = "/clear_cache_test.html"; 282 List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>(); 283 // Set Cache-Control headers to cache this request. One century should be long enough. 284 headers.add(Pair.create("Cache-Control", "max-age=3153600000")); 285 headers.add(Pair.create("Last-Modified", "Wed, 3 Oct 2012 00:00:00 GMT")); 286 final String pageUrl = webServer.setResponse( 287 pagePath, "<html><body>foo</body></html>", headers); 288 289 // First load to populate cache. 290 mActivityTestRule.clearCacheOnUiThread(awContents, true); 291 mActivityTestRule.loadUrlSync( 292 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 293 Assert.assertEquals(1, webServer.getRequestCount(pagePath)); 294 295 // Load about:blank so next load is not treated as reload by webkit and force 296 // revalidate with the server. 297 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 298 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 299 300 // No clearCache call, so should be loaded from cache. 301 mActivityTestRule.loadUrlSync( 302 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 303 Assert.assertEquals(1, webServer.getRequestCount(pagePath)); 304 305 // Same as above. 306 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 307 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 308 309 // Clear cache, so should hit server again. 310 mActivityTestRule.clearCacheOnUiThread(awContents, true); 311 mActivityTestRule.loadUrlSync( 312 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 313 Assert.assertEquals(2, webServer.getRequestCount(pagePath)); 314 } finally { 315 webServer.shutdown(); 316 } 317 } 318 319 @Test 320 @SmallTest 321 @Feature({"AndroidWebView"}) testClearCacheInQuickSuccession()322 public void testClearCacheInQuickSuccession() { 323 final AwTestContainerView testContainer = 324 mActivityTestRule.createAwTestContainerViewOnMainSync(new TestAwContentsClient()); 325 final AwContents awContents = testContainer.getAwContents(); 326 327 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { 328 for (int i = 0; i < 10; ++i) { 329 awContents.clearCache(true); 330 } 331 }); 332 } 333 334 @Test 335 @SmallTest 336 @Feature({"AndroidWebView"}) testGetFavicon()337 public void testGetFavicon() throws Throwable { 338 AwContents.setShouldDownloadFavicons(); 339 final AwTestContainerView testView = 340 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 341 final AwContents awContents = testView.getAwContents(); 342 343 TestWebServer webServer = TestWebServer.start(); 344 try { 345 final String faviconUrl = webServer.setResponseBase64( 346 "/" + CommonResources.FAVICON_FILENAME, CommonResources.FAVICON_DATA_BASE64, 347 CommonResources.getImagePngHeaders(false)); 348 final String pageUrl = webServer.setResponse("/favicon.html", 349 CommonResources.FAVICON_STATIC_HTML, null); 350 351 // The getFavicon will return the right icon a certain time after 352 // the page load completes which makes it slightly hard to test. 353 final Bitmap defaultFavicon = awContents.getFavicon(); 354 355 mActivityTestRule.getAwSettingsOnUiThread(awContents).setImagesEnabled(true); 356 mActivityTestRule.loadUrlSync( 357 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 358 359 mActivityTestRule.pollUiThread(() -> awContents.getFavicon() != null 360 && !awContents.getFavicon().sameAs(defaultFavicon)); 361 362 final Object originalFaviconSource = (new URL(faviconUrl)).getContent(); 363 final Bitmap originalFavicon = 364 BitmapFactory.decodeStream((InputStream) originalFaviconSource); 365 Assert.assertNotNull(originalFavicon); 366 367 Assert.assertTrue(awContents.getFavicon().sameAs(originalFavicon)); 368 369 } finally { 370 webServer.shutdown(); 371 } 372 } 373 374 @Test 375 @Feature({"AndroidWebView", "Downloads"}) 376 @SmallTest testDownload()377 public void testDownload() throws Throwable { 378 downloadAndCheck(null); 379 } 380 381 @Test 382 @Feature({"AndroidWebView", "Downloads"}) 383 @SmallTest testDownloadWithCustomUserAgent()384 public void testDownloadWithCustomUserAgent() throws Throwable { 385 downloadAndCheck("Custom User Agent"); 386 } 387 downloadAndCheck(String customUserAgent)388 private void downloadAndCheck(String customUserAgent) throws Throwable { 389 AwTestContainerView testView = 390 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 391 AwContents awContents = testView.getAwContents(); 392 393 if (customUserAgent != null) { 394 AwSettings awSettings = mActivityTestRule.getAwSettingsOnUiThread(awContents); 395 awSettings.setUserAgentString(customUserAgent); 396 } 397 398 final String data = "download data"; 399 final String contentDisposition = "attachment;filename=\"download.txt\""; 400 final String mimeType = "text/plain"; 401 402 List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>(); 403 downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition)); 404 downloadHeaders.add(Pair.create("Content-Type", mimeType)); 405 downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length()))); 406 407 TestWebServer webServer = TestWebServer.start(); 408 try { 409 final String pageUrl = webServer.setResponse( 410 "/download.txt", data, downloadHeaders); 411 final OnDownloadStartHelper downloadStartHelper = 412 mContentsClient.getOnDownloadStartHelper(); 413 final int callCount = downloadStartHelper.getCallCount(); 414 mActivityTestRule.loadUrlAsync(awContents, pageUrl); 415 downloadStartHelper.waitForCallback(callCount); 416 417 Assert.assertEquals(pageUrl, downloadStartHelper.getUrl()); 418 Assert.assertEquals(contentDisposition, downloadStartHelper.getContentDisposition()); 419 Assert.assertEquals(mimeType, downloadStartHelper.getMimeType()); 420 Assert.assertEquals(data.length(), downloadStartHelper.getContentLength()); 421 Assert.assertFalse(downloadStartHelper.getUserAgent().isEmpty()); 422 if (customUserAgent != null) { 423 Assert.assertEquals(customUserAgent, downloadStartHelper.getUserAgent()); 424 } else { 425 Assert.assertEquals( 426 downloadStartHelper.getUserAgent(), AwSettings.getDefaultUserAgent()); 427 } 428 } finally { 429 webServer.shutdown(); 430 } 431 } 432 433 @Test 434 @Feature({"AndroidWebView", "setNetworkAvailable"}) 435 @SmallTest testSetNetworkAvailable()436 public void testSetNetworkAvailable() throws Throwable { 437 AwTestContainerView testView = 438 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 439 AwContents awContents = testView.getAwContents(); 440 String script = "navigator.onLine"; 441 442 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 443 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 444 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 445 446 // Default to "online". 447 Assert.assertEquals("true", 448 mActivityTestRule.executeJavaScriptAndWaitForResult( 449 awContents, mContentsClient, script)); 450 451 // Forcing "offline". 452 AwActivityTestRule.setNetworkAvailableOnUiThread(awContents, false); 453 Assert.assertEquals("false", 454 mActivityTestRule.executeJavaScriptAndWaitForResult( 455 awContents, mContentsClient, script)); 456 457 // Forcing "online". 458 AwActivityTestRule.setNetworkAvailableOnUiThread(awContents, true); 459 Assert.assertEquals("true", 460 mActivityTestRule.executeJavaScriptAndWaitForResult( 461 awContents, mContentsClient, script)); 462 } 463 464 465 static class JavaScriptObject { 466 private CallbackHelper mCallbackHelper; JavaScriptObject(CallbackHelper callbackHelper)467 public JavaScriptObject(CallbackHelper callbackHelper) { 468 mCallbackHelper = callbackHelper; 469 } 470 471 @JavascriptInterface run()472 public void run() { 473 mCallbackHelper.notifyCalled(); 474 } 475 } 476 477 @Test 478 @Feature({"AndroidWebView", "Android-JavaBridge"}) 479 @SmallTest testJavaBridge()480 public void testJavaBridge() throws Throwable { 481 final AwTestContainerView testView = 482 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 483 final CallbackHelper callback = new CallbackHelper(); 484 485 AwContents awContents = testView.getAwContents(); 486 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 487 AwActivityTestRule.addJavascriptInterfaceOnUiThread( 488 awContents, new JavaScriptObject(callback), "bridge"); 489 mActivityTestRule.executeJavaScriptAndWaitForResult( 490 awContents, mContentsClient, "window.bridge.run();"); 491 callback.waitForCallback(0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 492 } 493 494 @Test 495 @Feature({"AndroidWebView"}) 496 @SmallTest testEscapingOfErrorPage()497 public void testEscapingOfErrorPage() throws Throwable { 498 AwTestContainerView testView = 499 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 500 AwContents awContents = testView.getAwContents(); 501 String script = "window.failed == true"; 502 503 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 504 CallbackHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper(); 505 int currentCallCount = onPageFinishedHelper.getCallCount(); 506 mActivityTestRule.loadUrlAsync(awContents, 507 "file:///file-that-does-not-exist#<script>window.failed = true;</script>"); 508 onPageFinishedHelper.waitForCallback( 509 currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 510 511 Assert.assertEquals("false", 512 mActivityTestRule.executeJavaScriptAndWaitForResult( 513 awContents, mContentsClient, script)); 514 } 515 516 @Test 517 @Feature({"AndroidWebView"}) 518 @SmallTest testCanInjectHeaders()519 public void testCanInjectHeaders() throws Throwable { 520 final AwTestContainerView testContainer = 521 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 522 final AwContents awContents = testContainer.getAwContents(); 523 524 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 525 526 EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer( 527 InstrumentationRegistry.getInstrumentation().getContext()); 528 529 try { 530 String url = testServer.getURL("/echoheader?X-foo"); 531 532 final Map<String, String> extraHeaders = new HashMap<String, String>(); 533 extraHeaders.put("X-foo", "bar"); 534 mActivityTestRule.loadUrlSync( 535 awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders); 536 537 String xfoo = mActivityTestRule.getJavaScriptResultBodyTextContent( 538 awContents, mContentsClient); 539 Assert.assertEquals("bar", xfoo); 540 541 url = testServer.getURL("/echoheader?Referer"); 542 543 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 544 url, ImmutableMap.of("Referer", "http://www.example.com/")); 545 546 String referer = mActivityTestRule.getJavaScriptResultBodyTextContent( 547 awContents, mContentsClient); 548 Assert.assertEquals("http://www.example.com/", referer); 549 } finally { 550 testServer.stopAndDestroyServer(); 551 } 552 } 553 554 // This is a meta test that we don't accidentally turn off hardware 555 // acceleration in instrumentation tests without notice. Do not add the 556 // @DisableHardwareAccelerationForTest annotation for this test. 557 @Test 558 @Feature({"AndroidWebView"}) 559 @SmallTest testHardwareModeWorks()560 public void testHardwareModeWorks() { 561 AwTestContainerView testContainer = 562 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 563 Assert.assertTrue(testContainer.isHardwareAccelerated()); 564 Assert.assertTrue(testContainer.isBackedByHardwareView()); 565 } 566 567 @Test 568 @Feature({"AndroidWebView"}) 569 @SmallTest testBasicCookieFunctionality()570 public void testBasicCookieFunctionality() throws Throwable { 571 AwTestContainerView testView = 572 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 573 AwContents awContents = testView.getAwContents(); 574 575 TestWebServer webServer = TestWebServer.start(); 576 try { 577 List<Pair<String, String>> responseHeaders = CommonResources.getTextHtmlHeaders(true); 578 final String cookie = "key=value"; 579 responseHeaders.add(Pair.create("Set-Cookie", cookie)); 580 final String url = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME, 581 CommonResources.ABOUT_HTML, responseHeaders); 582 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 583 mActivityTestRule.loadUrlSync( 584 awContents, mContentsClient.getOnPageFinishedHelper(), url); 585 586 final String script = "document.cookie"; 587 Assert.assertEquals("\"key=value\"", 588 mActivityTestRule.executeJavaScriptAndWaitForResult( 589 awContents, mContentsClient, script)); 590 } finally { 591 webServer.shutdown(); 592 } 593 } 594 595 /** 596 * Verifies that Web Notifications and the Push API are not exposed in WebView. 597 */ 598 @Test 599 @Feature({"AndroidWebView"}) 600 @SmallTest testPushAndNotificationsDisabled()601 public void testPushAndNotificationsDisabled() throws Throwable { 602 AwTestContainerView testView = 603 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 604 AwContents awContents = testView.getAwContents(); 605 606 String script = "window.Notification || window.PushManager"; 607 608 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 609 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 610 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 611 Assert.assertEquals("null", 612 mActivityTestRule.executeJavaScriptAndWaitForResult( 613 awContents, mContentsClient, script)); 614 } 615 getRendererPriorityOnUiThread(final AwContents awContents)616 private @RendererPriority int getRendererPriorityOnUiThread(final AwContents awContents) 617 throws Exception { 618 return TestThreadUtils.runOnUiThreadBlocking( 619 () -> awContents.getEffectivePriorityForTesting()); 620 } 621 setRendererPriorityOnUiThread(final AwContents awContents, final @RendererPriority int priority, final boolean waivedWhenNotVisible)622 private void setRendererPriorityOnUiThread(final AwContents awContents, 623 final @RendererPriority int priority, final boolean waivedWhenNotVisible) 624 throws Throwable { 625 mActivityTestRule.runOnUiThread( 626 () -> awContents.setRendererPriorityPolicy(priority, waivedWhenNotVisible)); 627 } 628 629 @Test 630 @Feature({"AndroidWebView"}) 631 @SmallTest 632 @OnlyRunIn(MULTI_PROCESS) 633 @CommandLineFlags.Add(ContentSwitches.RENDER_PROCESS_LIMIT + "=1") testForegroundPriorityOneProcess()634 public void testForegroundPriorityOneProcess() throws Throwable { 635 final AwTestContainerView view1 = 636 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 637 final AwContents contents1 = view1.getAwContents(); 638 final AwTestContainerView view2 = 639 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 640 final AwContents contents2 = view2.getAwContents(); 641 642 mActivityTestRule.loadUrlSync(contents1, mContentsClient.getOnPageFinishedHelper(), 643 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 644 mActivityTestRule.loadUrlSync(contents2, mContentsClient.getOnPageFinishedHelper(), 645 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 646 647 // Process should start out high. 648 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1)); 649 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2)); 650 651 // Set one to low. Process should take max priority of contents, so still high. 652 setRendererPriorityOnUiThread(contents1, RendererPriority.LOW, false); 653 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1)); 654 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2)); 655 656 // Set both to low and check. 657 setRendererPriorityOnUiThread(contents2, RendererPriority.LOW, false); 658 Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents1)); 659 Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents2)); 660 661 // Set both to waive and check. 662 setRendererPriorityOnUiThread(contents1, RendererPriority.WAIVED, false); 663 setRendererPriorityOnUiThread(contents2, RendererPriority.WAIVED, false); 664 Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents1)); 665 Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents2)); 666 667 // Set one to high and check. 668 setRendererPriorityOnUiThread(contents1, RendererPriority.HIGH, false); 669 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1)); 670 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2)); 671 672 // Destroy contents with high priority, and process should fall back to low. 673 // Destroy posts on UI, but getRendererPriorityOnUiThread posts after, so there should 674 // be no flakiness and no need for polling. 675 mActivityTestRule.destroyAwContentsOnMainSync(contents1); 676 Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents2)); 677 } 678 679 @Test 680 @Feature({"AndroidWebView"}) 681 @SmallTest 682 @OnlyRunIn(MULTI_PROCESS) 683 @CommandLineFlags.Add(ContentSwitches.RENDER_PROCESS_LIMIT + "=2") testForegroundPriorityTwoProcesses()684 public void testForegroundPriorityTwoProcesses() throws Throwable { 685 final AwTestContainerView view1 = 686 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 687 final AwContents contents1 = view1.getAwContents(); 688 final AwTestContainerView view2 = 689 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 690 final AwContents contents2 = view2.getAwContents(); 691 692 mActivityTestRule.loadUrlSync(contents1, mContentsClient.getOnPageFinishedHelper(), 693 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 694 mActivityTestRule.loadUrlSync(contents2, mContentsClient.getOnPageFinishedHelper(), 695 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 696 697 // Process should start out high. 698 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1)); 699 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2)); 700 701 // Set one to low. Other should not be affected. 702 setRendererPriorityOnUiThread(contents1, RendererPriority.LOW, false); 703 Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents1)); 704 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2)); 705 } 706 707 @Test 708 @Feature({"AndroidWebView"}) 709 @SmallTest 710 @OnlyRunIn(MULTI_PROCESS) testBackgroundPriority()711 public void testBackgroundPriority() throws Throwable { 712 final AwContents awContents = 713 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient) 714 .getAwContents(); 715 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents)); 716 717 mActivityTestRule.runOnUiThread(() -> awContents.onPause()); 718 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents)); 719 720 setRendererPriorityOnUiThread( 721 awContents, RendererPriority.HIGH, true /* waivedWhenNotVisible */); 722 Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(awContents)); 723 724 mActivityTestRule.runOnUiThread(() -> awContents.onResume()); 725 Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents)); 726 } 727 728 @Test 729 @Feature({"AndroidWebView"}) 730 @SmallTest 731 @OnlyRunIn(MULTI_PROCESS) testPauseDestroyResume()732 public void testPauseDestroyResume() throws Throwable { 733 mActivityTestRule.runOnUiThread(() -> { 734 AwContents awContents; 735 awContents = mActivityTestRule.createAwTestContainerView(mContentsClient) 736 .getAwContents(); 737 awContents.pauseTimers(); 738 awContents.pauseTimers(); 739 awContents.destroy(); 740 awContents = mActivityTestRule.createAwTestContainerView(mContentsClient) 741 .getAwContents(); 742 awContents.resumeTimers(); 743 }); 744 } 745 getRenderProcessOnUiThread(final AwContents awContents)746 private AwRenderProcess getRenderProcessOnUiThread(final AwContents awContents) 747 throws Exception { 748 return TestThreadUtils.runOnUiThreadBlocking(() -> awContents.getRenderProcess()); 749 } 750 751 @Test 752 @Feature({"AndroidWebView"}) 753 @SmallTest 754 @OnlyRunIn(MULTI_PROCESS) testRenderProcessInMultiProcessMode()755 public void testRenderProcessInMultiProcessMode() throws Throwable { 756 AwTestContainerView testView = 757 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 758 final AwContents awContents = testView.getAwContents(); 759 760 final AwRenderProcess preLoadRenderProcess = getRenderProcessOnUiThread(awContents); 761 Assert.assertNotNull(preLoadRenderProcess); 762 763 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 764 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 765 766 final AwRenderProcess postLoadRenderProcess = getRenderProcessOnUiThread(awContents); 767 Assert.assertEquals(preLoadRenderProcess, postLoadRenderProcess); 768 } 769 770 @Test 771 @Feature({"AndroidWebView"}) 772 @SmallTest 773 @OnlyRunIn(SINGLE_PROCESS) testNoRenderProcessInSingleProcessMode()774 public void testNoRenderProcessInSingleProcessMode() throws Throwable { 775 AwTestContainerView testView = 776 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 777 final AwContents awContents = testView.getAwContents(); 778 779 mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), 780 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 781 782 final AwRenderProcess renderProcess = getRenderProcessOnUiThread(awContents); 783 Assert.assertEquals(renderProcess, null); 784 } 785 786 /** Regression test for https://crbug.com/732976. Load a data URL, then immediately 787 * after that load a javascript URL. The data URL navigation shouldn't be blocked. 788 */ 789 @Test 790 @LargeTest 791 @Feature({"AndroidWebView"}) testJavaScriptUrlAfterLoadData()792 public void testJavaScriptUrlAfterLoadData() throws Throwable { 793 AwTestContainerView testView = 794 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 795 final AwContents awContents = testView.getAwContents(); 796 mActivityTestRule.runOnUiThread(() -> { 797 // Run javascript navigation immediately, without waiting for the completion of data 798 // URL. 799 awContents.loadData("<html>test</html>", "text/html", "utf-8"); 800 awContents.loadUrl("javascript: void(0)"); 801 }); 802 803 mContentsClient.getOnPageFinishedHelper().waitForCallback( 804 0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 805 Assert.assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl()); 806 807 TestAwContentsClient.AddMessageToConsoleHelper consoleHelper = 808 mContentsClient.getAddMessageToConsoleHelper(); 809 Assert.assertEquals(0, consoleHelper.getMessages().size()); 810 } 811 812 /** 813 * Regression test for https://crbug.com/1145717. Load a URL that requires fixing and verify 814 * that the legacy behavior is preserved (i.e. that the URL is fixed + that no crashes happen in 815 * the product). 816 * 817 * The main test verification is that there are no crashes. In particular, this test tries 818 * to verify that the `loadUrl` call above won't trigger: 819 * - NOTREACHED and DwoC in content::NavigationRequest's constructor for about: scheme 820 * navigations that aren't about:blank nor about:srcdoc 821 * - CHECK in content::NavigationRequest::GetOriginForURLLoaderFactory caused by the 822 * mismatch between the result of this method and the "about:" process lock. 823 */ 824 @Test 825 @LargeTest 826 @Feature({"AndroidWebView"}) testLoadUrlAboutVersion()827 public void testLoadUrlAboutVersion() throws Throwable { 828 AwTestContainerView testView = 829 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 830 final AwContents awContents = testView.getAwContents(); 831 mActivityTestRule.runOnUiThread(() -> { 832 // "about:safe-browsing" will be rewritten by 833 // components.url_formatter.UrlFormatter.fixupUrl into 834 // "chrome://safe-browsing/". 835 // 836 // Note that chrome://safe-browsing/ is one of very few chrome://... URLs that work 837 // in Android WebView. In particular, chrome://version/ wouldn't work. 838 awContents.loadUrl("about:safe-browsing"); 839 }); 840 841 mContentsClient.getOnPageFinishedHelper().waitForCallback( 842 0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 843 Assert.assertEquals("chrome://safe-browsing/", awContents.getLastCommittedUrl()); 844 } 845 doHardwareRenderingSmokeTest()846 private void doHardwareRenderingSmokeTest() throws Throwable { 847 AwTestContainerView testView = 848 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 849 final AwContents awContents = testView.getAwContents(); 850 String html = "<html>" 851 + " <body style=\"" 852 + " padding: 0;" 853 + " margin: 0;" 854 + " display: grid;" 855 + " display: grid;" 856 + " grid-template-columns: 50% 50%;" 857 + " grid-template-rows: 50% 50%;\">" 858 + " <div style=\"background-color: rgb(255, 0, 0);\"></div>" 859 + " <div style=\"background-color: rgb(0, 255, 0);\"></div>" 860 + " <div style=\"background-color: rgb(0, 0, 255);\"></div>" 861 + " <div style=\"background-color: rgb(128, 128, 128);\"></div>" 862 + " </body>" 863 + "</html>"; 864 mActivityTestRule.loadDataSync(testView.getAwContents(), 865 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false); 866 mActivityTestRule.waitForVisualStateCallback(testView.getAwContents()); 867 868 int[] lastQuadrantColors = null; 869 // Poll for 10s in case raster is slow. 870 for (int i = 0; i < 100; ++i) { 871 final CallbackHelper callbackHelper = new CallbackHelper(); 872 final Object[] resultHolder = new Object[1]; 873 mActivityTestRule.runOnUiThread(() -> { 874 testView.readbackQuadrantColors((int[] result) -> { 875 resultHolder[0] = result; 876 callbackHelper.notifyCalled(); 877 }); 878 }); 879 try { 880 callbackHelper.waitForFirst(); 881 } catch (TimeoutException e) { 882 Log.w(TAG, "Timeout", e); 883 continue; 884 } 885 int[] quadrantColors = (int[]) resultHolder[0]; 886 lastQuadrantColors = quadrantColors; 887 if (quadrantColors != null && Color.rgb(255, 0, 0) == quadrantColors[0] 888 && Color.rgb(0, 255, 0) == quadrantColors[1] 889 && Color.rgb(0, 0, 255) == quadrantColors[2] 890 && Color.rgb(128, 128, 128) == quadrantColors[3]) { 891 return; 892 } 893 Thread.sleep(100); 894 } 895 Assert.assertNotNull(lastQuadrantColors); 896 // If this test is failing for your CL, then chances are your change is breaking Android 897 // WebView hardware rendering. Please build the "real" webview and check if this is the 898 // case and if so, fix your CL. 899 Assert.assertEquals(Color.rgb(255, 0, 0), lastQuadrantColors[0]); 900 Assert.assertEquals(Color.rgb(0, 255, 0), lastQuadrantColors[1]); 901 Assert.assertEquals(Color.rgb(0, 0, 255), lastQuadrantColors[2]); 902 Assert.assertEquals(Color.rgb(128, 128, 128), lastQuadrantColors[3]); 903 } 904 905 @Test 906 @Feature({"AndroidWebView"}) 907 @MediumTest testHardwareRenderingSmokeTest()908 public void testHardwareRenderingSmokeTest() throws Throwable { 909 doHardwareRenderingSmokeTest(); 910 } 911 912 @Test 913 @Feature({"AndroidWebView"}) 914 @MediumTest 915 @CommandLineFlags. 916 Add({"enable-features=" + VizFeatures.USE_SKIA_RENDERER, "disable-oop-rasterization"}) testHardwareRenderingSmokeTestSkiaRenderer()917 public void testHardwareRenderingSmokeTestSkiaRenderer() throws Throwable { 918 doHardwareRenderingSmokeTest(); 919 } 920 921 @Test 922 @Feature({"AndroidWebView"}) 923 @SmallTest testFixupOctothorpesInLoadDataContent()924 public void testFixupOctothorpesInLoadDataContent() { 925 // If there are no octothorpes the function should have no effect. 926 final String noOctothorpeString = "<div id='foo1'>This content has no octothorpe</div>"; 927 Assert.assertEquals(noOctothorpeString, 928 AwContents.fixupOctothorpesInLoadDataContent(noOctothorpeString)); 929 930 // One '#' followed by a valid DOM id requires us to duplicate it into a real fragment. 931 Assert.assertEquals("abc%23A#A", AwContents.fixupOctothorpesInLoadDataContent("abc#A")); 932 Assert.assertEquals("abc%23a#a", AwContents.fixupOctothorpesInLoadDataContent("abc#a")); 933 Assert.assertEquals("abc%23Aa#Aa", AwContents.fixupOctothorpesInLoadDataContent("abc#Aa")); 934 Assert.assertEquals("abc%23aA#aA", AwContents.fixupOctothorpesInLoadDataContent("abc#aA")); 935 Assert.assertEquals( 936 "abc%23a1-_:.#a1-_:.", AwContents.fixupOctothorpesInLoadDataContent("abc#a1-_:.")); 937 938 // One '#' followed by an invalid DOM id just means we encode the '#'. 939 Assert.assertEquals("abc%231", AwContents.fixupOctothorpesInLoadDataContent("abc#1")); 940 Assert.assertEquals("abc%231a", AwContents.fixupOctothorpesInLoadDataContent("abc#1a")); 941 Assert.assertEquals( 942 "abc%23not valid", AwContents.fixupOctothorpesInLoadDataContent("abc#not valid")); 943 Assert.assertEquals("abc%23a@", AwContents.fixupOctothorpesInLoadDataContent("abc#a@")); 944 945 // Multiple '#', whether or not they have a valid DOM id afterwards, just means we encode 946 // the '#'. 947 Assert.assertEquals("abc%23%23a", AwContents.fixupOctothorpesInLoadDataContent("abc##a")); 948 Assert.assertEquals("abc%23a%23b", AwContents.fixupOctothorpesInLoadDataContent("abc#a#b")); 949 } 950 951 @Test 952 @Feature({"AndroidWebView"}) 953 @SmallTest testLoadDataOctothorpeHandling()954 public void testLoadDataOctothorpeHandling() throws Throwable { 955 AwTestContainerView testView = 956 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 957 final AwContents awContents = testView.getAwContents(); 958 959 // Before Android Q, the loadData API is expected to handle the encoding for users. 960 boolean encodeOctothorpes = !BuildInfo.targetsAtLeastQ(); 961 962 // A URL with no '#' character. 963 mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), 964 "<html>test</html>", "text/html", false); 965 Assert.assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl()); 966 967 // A URL with one '#' character. 968 mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), 969 "<html>test#foo</html>", "text/html", false); 970 String expectedUrl = encodeOctothorpes ? "data:text/html,<html>test%23foo</html>" 971 : "data:text/html,<html>test#foo</html>"; 972 Assert.assertEquals(expectedUrl, awContents.getLastCommittedUrl()); 973 974 // A URL with many '#' characters. 975 mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), 976 "<html>test#foo#bar#</html>", "text/html", false); 977 expectedUrl = encodeOctothorpes ? "data:text/html,<html>test%23foo%23bar%23</html>" 978 : "data:text/html,<html>test#foo#bar#</html>"; 979 Assert.assertEquals(expectedUrl, awContents.getLastCommittedUrl()); 980 981 // An already encoded '#' character. 982 mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), 983 "<html>test%23foo</html>", "text/html", false); 984 Assert.assertEquals( 985 "data:text/html,<html>test%23foo</html>", awContents.getLastCommittedUrl()); 986 987 // A URL with a valid fragment. Before Q, this must be manipulated so that it renders the 988 // same and still scrolls to the fragment location. 989 if (encodeOctothorpes) { 990 String contents = "<div style='height: 5000px'></div><a id='target'>Target</a>#target"; 991 mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), 992 contents, "text/html", false); 993 Assert.assertEquals( 994 "data:text/html,<div style='height: 5000px'></div><a id='target'>Target</a>" 995 + "%23target#target", 996 awContents.getLastCommittedUrl()); 997 // TODO(smcgruer): I can physically see that this has scrolled on the test page, and 998 // have traced scrolling through PaintLayerScrollableArea, but I don't know how to check 999 // it. 1000 } 1001 } 1002 getHistogramSampleCount(String name)1003 private int getHistogramSampleCount(String name) { 1004 TestThreadUtils.runOnUiThreadBlocking(() -> { 1005 mHistogramTotalCount = RecordHistogram.getHistogramTotalCountForTesting(name); 1006 }); 1007 return mHistogramTotalCount; 1008 } 1009 1010 @Test 1011 @Feature({"AndroidWebView"}) 1012 @SmallTest testLoadUrlRecordsScheme_http()1013 public void testLoadUrlRecordsScheme_http() { 1014 // No need to spin up a web server, since we don't care if the load ever succeeds. 1015 final String httpUrlWithNoRealPage = "http://some.origin/some/path.html"; 1016 loadUrlAndCheckScheme(httpUrlWithNoRealPage, AwContents.UrlScheme.HTTP_SCHEME); 1017 } 1018 1019 @Test 1020 @Feature({"AndroidWebView"}) 1021 @SmallTest testLoadUrlRecordsScheme_javascript()1022 public void testLoadUrlRecordsScheme_javascript() { 1023 loadUrlAndCheckScheme( 1024 "javascript:console.log('message')", AwContents.UrlScheme.JAVASCRIPT_SCHEME); 1025 } 1026 1027 @Test 1028 @Feature({"AndroidWebView"}) 1029 @SmallTest testLoadUrlRecordsScheme_fileAndroidAsset()1030 public void testLoadUrlRecordsScheme_fileAndroidAsset() { 1031 loadUrlAndCheckScheme("file:///android_asset/some/asset/page.html", 1032 AwContents.UrlScheme.FILE_ANDROID_ASSET_SCHEME); 1033 } 1034 1035 @Test 1036 @Feature({"AndroidWebView"}) 1037 @SmallTest testLoadUrlRecordsScheme_fileRegular()1038 public void testLoadUrlRecordsScheme_fileRegular() { 1039 loadUrlAndCheckScheme("file:///some/path/on/disk.html", AwContents.UrlScheme.FILE_SCHEME); 1040 } 1041 1042 @Test 1043 @Feature({"AndroidWebView"}) 1044 @SmallTest testLoadUrlRecordsScheme_data()1045 public void testLoadUrlRecordsScheme_data() { 1046 loadUrlAndCheckScheme( 1047 "data:text/html,<html><body>foo</body></html>", AwContents.UrlScheme.DATA_SCHEME); 1048 } 1049 1050 @Test 1051 @Feature({"AndroidWebView"}) 1052 @SmallTest testLoadUrlRecordsScheme_blank()1053 public void testLoadUrlRecordsScheme_blank() { 1054 loadUrlAndCheckScheme("about:blank", AwContents.UrlScheme.EMPTY); 1055 } 1056 loadUrlAndCheckScheme(String url, @AwContents.UrlScheme int expectedSchemeEnum)1057 private void loadUrlAndCheckScheme(String url, @AwContents.UrlScheme int expectedSchemeEnum) { 1058 AwTestContainerView testView = 1059 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 1060 final AwContents awContents = testView.getAwContents(); 1061 1062 Assert.assertEquals(0, 1063 RecordHistogram.getHistogramTotalCountForTesting( 1064 AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME)); 1065 // Note: we use async because not all loads emit onPageFinished. This relies on the UMA 1066 // metric being logged in the synchronous part of loadUrl(). 1067 mActivityTestRule.loadUrlAsync(awContents, url); 1068 Assert.assertEquals(1, 1069 RecordHistogram.getHistogramTotalCountForTesting( 1070 AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME)); 1071 Assert.assertEquals(1, 1072 RecordHistogram.getHistogramValueCountForTesting( 1073 AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME, expectedSchemeEnum)); 1074 } 1075 1076 @Test 1077 @Feature({"AndroidWebView"}) 1078 @SmallTest testFindAllAsyncEmptySearchString()1079 public void testFindAllAsyncEmptySearchString() { 1080 AwTestContainerView testView = 1081 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 1082 final AwContents awContents = testView.getAwContents(); 1083 try { 1084 awContents.findAllAsync(null); 1085 Assert.fail("A null searchString should cause an exception to be thrown"); 1086 } catch (IllegalArgumentException e) { 1087 // expected 1088 } 1089 } 1090 1091 @Test 1092 @Feature({"AndroidWebView"}) 1093 @SmallTest testInsertNullVisualStateCallback()1094 public void testInsertNullVisualStateCallback() { 1095 AwTestContainerView testView = 1096 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 1097 final AwContents awContents = testView.getAwContents(); 1098 try { 1099 awContents.insertVisualStateCallback(0, null); 1100 Assert.fail("A null VisualStateCallback should cause an exception to be thrown"); 1101 } catch (IllegalArgumentException e) { 1102 // expected 1103 } 1104 } 1105 1106 private static final String HELLO_WORLD_URL = "/android_webview/test/data/hello_world.html"; 1107 private static final String HELLO_WORLD_TITLE = "Hello, World!"; 1108 private static final String WEBUI_URL = "chrome://safe-browsing"; 1109 private static final String WEBUI_TITLE = "Safe Browsing"; 1110 1111 // Check that we can navigate between a regular web page and a WebUI page 1112 // that's available on AW (chrome://safe-browsing), and that the WebUI page 1113 // loads in its own locked renderer process when in multi-process mode. 1114 @Test 1115 @Feature({"AndroidWebView"}) 1116 @SmallTest 1117 @OnlyRunIn(MULTI_PROCESS) testWebUIUsesDedicatedProcessInMultiProcessMode()1118 public void testWebUIUsesDedicatedProcessInMultiProcessMode() throws Throwable { 1119 AwTestContainerView testView = 1120 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 1121 final AwContents awContents = testView.getAwContents(); 1122 1123 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 1124 1125 EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer( 1126 InstrumentationRegistry.getInstrumentation().getContext()); 1127 try { 1128 final String pageUrl = testServer.getURL(HELLO_WORLD_URL); 1129 1130 mActivityTestRule.loadUrlSync( 1131 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 1132 Assert.assertEquals( 1133 HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents)); 1134 1135 final AwRenderProcess rendererProcess1 = getRenderProcessOnUiThread(awContents); 1136 Assert.assertNotNull(rendererProcess1); 1137 1138 // Until AW gets site isolation, ordinary web content should not be 1139 // locked to origin. 1140 boolean isLocked = TestThreadUtils.runOnUiThreadBlocking( 1141 () -> rendererProcess1.isProcessLockedToSiteForTesting()); 1142 Assert.assertFalse("Initial renderer process should not be locked", isLocked); 1143 1144 mActivityTestRule.loadUrlSync( 1145 awContents, mContentsClient.getOnPageFinishedHelper(), WEBUI_URL); 1146 Assert.assertEquals(WEBUI_TITLE, mActivityTestRule.getTitleOnUiThread(awContents)); 1147 1148 final AwRenderProcess webuiProcess = getRenderProcessOnUiThread(awContents); 1149 Assert.assertNotEquals(rendererProcess1, webuiProcess); 1150 // WebUI pages should be locked to origin even on AW. 1151 isLocked = TestThreadUtils.runOnUiThreadBlocking( 1152 () -> webuiProcess.isProcessLockedToSiteForTesting()); 1153 Assert.assertTrue("WebUI process should be locked", isLocked); 1154 1155 mActivityTestRule.loadUrlSync( 1156 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 1157 1158 final AwRenderProcess rendererProcess2 = getRenderProcessOnUiThread(awContents); 1159 Assert.assertEquals( 1160 HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents)); 1161 Assert.assertNotEquals(rendererProcess2, webuiProcess); 1162 isLocked = TestThreadUtils.runOnUiThreadBlocking( 1163 () -> rendererProcess2.isProcessLockedToSiteForTesting()); 1164 Assert.assertFalse("Final renderer process should not be locked", isLocked); 1165 } finally { 1166 testServer.stopAndDestroyServer(); 1167 } 1168 } 1169 1170 // In single-process mode, navigations to WebUI should work, but WebUI does 1171 // not gets process-isolated. 1172 @Test 1173 @Feature({"AndroidWebView"}) 1174 @SmallTest 1175 @OnlyRunIn(SINGLE_PROCESS) testWebUILoadsWithoutProcessIsolationInSingleProcessMode()1176 public void testWebUILoadsWithoutProcessIsolationInSingleProcessMode() throws Throwable { 1177 AwTestContainerView testView = 1178 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 1179 final AwContents awContents = testView.getAwContents(); 1180 1181 AwActivityTestRule.enableJavaScriptOnUiThread(awContents); 1182 1183 EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer( 1184 InstrumentationRegistry.getInstrumentation().getContext()); 1185 try { 1186 final String pageUrl = testServer.getURL(HELLO_WORLD_URL); 1187 1188 mActivityTestRule.loadUrlSync( 1189 awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl); 1190 Assert.assertEquals( 1191 HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents)); 1192 1193 final AwRenderProcess rendererProcess1 = getRenderProcessOnUiThread(awContents); 1194 Assert.assertNull(rendererProcess1); 1195 1196 mActivityTestRule.loadUrlSync( 1197 awContents, mContentsClient.getOnPageFinishedHelper(), WEBUI_URL); 1198 Assert.assertEquals(WEBUI_TITLE, mActivityTestRule.getTitleOnUiThread(awContents)); 1199 1200 final AwRenderProcess webuiProcess = getRenderProcessOnUiThread(awContents); 1201 Assert.assertNull(webuiProcess); 1202 } finally { 1203 testServer.stopAndDestroyServer(); 1204 } 1205 } 1206 } 1207