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.weblayer.test; 6 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertFalse; 9 import static org.junit.Assert.assertNotEquals; 10 import static org.junit.Assert.assertNotNull; 11 import static org.junit.Assert.assertTrue; 12 13 import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; 14 15 import android.net.Uri; 16 import android.os.SystemClock; 17 import android.support.test.InstrumentationRegistry; 18 import android.webkit.WebResourceResponse; 19 20 import androidx.test.filters.SmallTest; 21 22 import org.hamcrest.Matchers; 23 import org.junit.Assert; 24 import org.junit.Before; 25 import org.junit.Rule; 26 import org.junit.Test; 27 import org.junit.runner.RunWith; 28 29 import org.chromium.base.test.util.CallbackHelper; 30 import org.chromium.base.test.util.Criteria; 31 import org.chromium.base.test.util.CriteriaHelper; 32 import org.chromium.content_public.browser.test.util.TestThreadUtils; 33 import org.chromium.net.test.util.TestWebServer; 34 import org.chromium.weblayer.Browser; 35 import org.chromium.weblayer.LoadError; 36 import org.chromium.weblayer.NavigateParams; 37 import org.chromium.weblayer.Navigation; 38 import org.chromium.weblayer.NavigationCallback; 39 import org.chromium.weblayer.NavigationController; 40 import org.chromium.weblayer.NavigationState; 41 import org.chromium.weblayer.Tab; 42 import org.chromium.weblayer.TabCallback; 43 import org.chromium.weblayer.TabListCallback; 44 import org.chromium.weblayer.WebLayer; 45 import org.chromium.weblayer.shell.InstrumentationActivity; 46 47 import java.io.ByteArrayInputStream; 48 import java.io.InputStream; 49 import java.nio.charset.StandardCharsets; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.Collections; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.concurrent.TimeoutException; 57 import java.util.concurrent.atomic.AtomicReference; 58 59 /** 60 * Example test that just starts the weblayer shell. 61 */ 62 @RunWith(WebLayerJUnit4ClassRunner.class) 63 public class NavigationTest { 64 @Rule 65 public InstrumentationActivityTestRule mActivityTestRule = 66 new InstrumentationActivityTestRule(); 67 68 // URLs used for base tests. 69 private static final String URL1 = "data:text,foo"; 70 private static final String URL2 = "data:text,bar"; 71 private static final String URL3 = "data:text,baz"; 72 private static final String URL4 = "data:text,bat"; 73 private static final String STREAM_URL = "https://doesntreallyexist123.com/bar"; 74 private static final String STREAM_HTML = "<html>foobar</html>"; 75 private static final String STREAM_INNER_BODY = "foobar"; 76 77 private static boolean sShouldTrackPageInitiated; 78 79 private static class Callback extends NavigationCallback { 80 public static class NavigationCallbackHelper extends CallbackHelper { 81 private Uri mUri; 82 private boolean mIsSameDocument; 83 private int mHttpStatusCode; 84 private List<Uri> mRedirectChain; 85 private @LoadError int mLoadError; 86 private @NavigationState int mNavigationState; 87 private boolean mIsPageInitiatedNavigation; 88 notifyCalled(Navigation navigation)89 public void notifyCalled(Navigation navigation) { 90 mUri = navigation.getUri(); 91 mIsSameDocument = navigation.isSameDocument(); 92 mHttpStatusCode = navigation.getHttpStatusCode(); 93 mRedirectChain = navigation.getRedirectChain(); 94 mLoadError = navigation.getLoadError(); 95 mNavigationState = navigation.getState(); 96 if (sShouldTrackPageInitiated) { 97 mIsPageInitiatedNavigation = navigation.isPageInitiated(); 98 } 99 notifyCalled(); 100 } 101 assertCalledWith(int currentCallCount, String uri)102 public void assertCalledWith(int currentCallCount, String uri) throws TimeoutException { 103 waitForCallback(currentCallCount); 104 assertEquals(mUri.toString(), uri); 105 } 106 assertCalledWith(int currentCallCount, String uri, boolean isSameDocument)107 public void assertCalledWith(int currentCallCount, String uri, boolean isSameDocument) 108 throws TimeoutException { 109 waitForCallback(currentCallCount); 110 assertEquals(mUri.toString(), uri); 111 assertEquals(mIsSameDocument, isSameDocument); 112 } 113 assertCalledWith(int currentCallCount, List<Uri> redirectChain)114 public void assertCalledWith(int currentCallCount, List<Uri> redirectChain) 115 throws TimeoutException { 116 waitForCallback(currentCallCount); 117 assertEquals(mRedirectChain, redirectChain); 118 } 119 assertCalledWith(int currentCallCount, String uri, @LoadError int loadError)120 public void assertCalledWith(int currentCallCount, String uri, @LoadError int loadError) 121 throws TimeoutException { 122 waitForCallback(currentCallCount); 123 assertEquals(mUri.toString(), uri); 124 assertEquals(mLoadError, loadError); 125 } 126 getHttpStatusCode()127 public int getHttpStatusCode() { 128 return mHttpStatusCode; 129 } 130 131 @NavigationState getNavigationState()132 public int getNavigationState() { 133 return mNavigationState; 134 } 135 isPageInitiated()136 public boolean isPageInitiated() { 137 assert sShouldTrackPageInitiated; 138 return mIsPageInitiatedNavigation; 139 } 140 } 141 142 public static class UriCallbackHelper extends CallbackHelper { 143 private Uri mUri; 144 notifyCalled(Uri uri)145 public void notifyCalled(Uri uri) { 146 mUri = uri; 147 notifyCalled(); 148 } 149 getUri()150 public Uri getUri() { 151 return mUri; 152 } 153 } 154 155 public static class NavigationCallbackValueRecorder { 156 private List<String> mObservedValues = 157 Collections.synchronizedList(new ArrayList<String>()); 158 recordValue(String parameter)159 public void recordValue(String parameter) { 160 mObservedValues.add(parameter); 161 } 162 getObservedValues()163 public List<String> getObservedValues() { 164 return mObservedValues; 165 } 166 waitUntilValueObserved(String expectation)167 public void waitUntilValueObserved(String expectation) { 168 CriteriaHelper.pollInstrumentationThread( 169 () -> Criteria.checkThat(expectation, Matchers.isIn(mObservedValues))); 170 } 171 } 172 173 public static class FirstContentfulPaintCallbackHelper extends CallbackHelper { 174 private long mNavigationStartMillis; 175 private long mFirstContentfulPaintMs; 176 notifyCalled(long navigationStartMillis, long firstContentfulPaintMs)177 public void notifyCalled(long navigationStartMillis, long firstContentfulPaintMs) { 178 mNavigationStartMillis = navigationStartMillis; 179 mFirstContentfulPaintMs = firstContentfulPaintMs; 180 notifyCalled(); 181 } 182 getNavigationStartMillis()183 public long getNavigationStartMillis() { 184 return mNavigationStartMillis; 185 } 186 getFirstContentfulPaintMs()187 public long getFirstContentfulPaintMs() { 188 return mFirstContentfulPaintMs; 189 } 190 } 191 192 public static class LargestContentfulPaintCallbackHelper extends CallbackHelper { 193 private long mNavigationStartMillis; 194 private long mLargestContentfulPaintMs; 195 notifyCalled(long navigationStartMillis, long largestContentfulPaintMs)196 public void notifyCalled(long navigationStartMillis, long largestContentfulPaintMs) { 197 mNavigationStartMillis = navigationStartMillis; 198 mLargestContentfulPaintMs = largestContentfulPaintMs; 199 notifyCalled(); 200 } 201 getNavigationStartMillis()202 public long getNavigationStartMillis() { 203 return mNavigationStartMillis; 204 } 205 getLargestContentfulPaintMs()206 public long getLargestContentfulPaintMs() { 207 return mLargestContentfulPaintMs; 208 } 209 } 210 211 public NavigationCallbackHelper onStartedCallback = new NavigationCallbackHelper(); 212 public NavigationCallbackHelper onRedirectedCallback = new NavigationCallbackHelper(); 213 public NavigationCallbackHelper onReadyToCommitCallback = new NavigationCallbackHelper(); 214 public NavigationCallbackHelper onCompletedCallback = new NavigationCallbackHelper(); 215 public NavigationCallbackHelper onFailedCallback = new NavigationCallbackHelper(); 216 public NavigationCallbackValueRecorder loadStateChangedCallback = 217 new NavigationCallbackValueRecorder(); 218 public NavigationCallbackValueRecorder loadProgressChangedCallback = 219 new NavigationCallbackValueRecorder(); 220 public CallbackHelper onFirstContentfulPaintCallback = new CallbackHelper(); 221 public FirstContentfulPaintCallbackHelper onFirstContentfulPaint2Callback = 222 new FirstContentfulPaintCallbackHelper(); 223 public LargestContentfulPaintCallbackHelper onLargestContentfulPaintCallback = 224 new LargestContentfulPaintCallbackHelper(); 225 public UriCallbackHelper onOldPageNoLongerRenderedCallback = new UriCallbackHelper(); 226 227 @Override onNavigationStarted(Navigation navigation)228 public void onNavigationStarted(Navigation navigation) { 229 onStartedCallback.notifyCalled(navigation); 230 } 231 232 @Override onNavigationRedirected(Navigation navigation)233 public void onNavigationRedirected(Navigation navigation) { 234 onRedirectedCallback.notifyCalled(navigation); 235 } 236 237 @Override onReadyToCommitNavigation(Navigation navigation)238 public void onReadyToCommitNavigation(Navigation navigation) { 239 onReadyToCommitCallback.notifyCalled(navigation); 240 } 241 242 @Override onNavigationCompleted(Navigation navigation)243 public void onNavigationCompleted(Navigation navigation) { 244 onCompletedCallback.notifyCalled(navigation); 245 } 246 247 @Override onNavigationFailed(Navigation navigation)248 public void onNavigationFailed(Navigation navigation) { 249 onFailedCallback.notifyCalled(navigation); 250 } 251 252 @Override onFirstContentfulPaint()253 public void onFirstContentfulPaint() { 254 onFirstContentfulPaintCallback.notifyCalled(); 255 } 256 257 @Override onFirstContentfulPaint( long navigationStartMillis, long firstContentfulPaintMs)258 public void onFirstContentfulPaint( 259 long navigationStartMillis, long firstContentfulPaintMs) { 260 onFirstContentfulPaint2Callback.notifyCalled( 261 navigationStartMillis, firstContentfulPaintMs); 262 } 263 264 @Override onLargestContentfulPaint( long navigationStartMillis, long largestContentfulPaintMs)265 public void onLargestContentfulPaint( 266 long navigationStartMillis, long largestContentfulPaintMs) { 267 onLargestContentfulPaintCallback.notifyCalled( 268 navigationStartMillis, largestContentfulPaintMs); 269 } 270 271 @Override onOldPageNoLongerRendered(Uri newNavigationUri)272 public void onOldPageNoLongerRendered(Uri newNavigationUri) { 273 onOldPageNoLongerRenderedCallback.notifyCalled(newNavigationUri); 274 } 275 276 @Override onLoadStateChanged(boolean isLoading, boolean toDifferentDocument)277 public void onLoadStateChanged(boolean isLoading, boolean toDifferentDocument) { 278 loadStateChangedCallback.recordValue( 279 Boolean.toString(isLoading) + " " + Boolean.toString(toDifferentDocument)); 280 } 281 282 @Override onLoadProgressChanged(double progress)283 public void onLoadProgressChanged(double progress) { 284 loadProgressChangedCallback.recordValue( 285 progress == 1 ? "load complete" : "load started"); 286 } 287 } 288 289 private final Callback mCallback = new Callback(); 290 291 @Before setUp()292 public void setUp() throws Throwable { 293 TestThreadUtils.runOnUiThreadBlocking(() -> { 294 sShouldTrackPageInitiated = 295 WebLayer.getSupportedMajorVersion( 296 InstrumentationRegistry.getTargetContext().getApplicationContext()) 297 >= 86; 298 }); 299 } 300 301 @Test 302 @SmallTest testNavigationEvents()303 public void testNavigationEvents() throws Exception { 304 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 305 306 setNavigationCallback(activity); 307 int curStartedCount = mCallback.onStartedCallback.getCallCount(); 308 int curCommittedCount = mCallback.onReadyToCommitCallback.getCallCount(); 309 int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); 310 int curOnFirstContentfulPaintCount = 311 mCallback.onFirstContentfulPaintCallback.getCallCount(); 312 313 mActivityTestRule.navigateAndWait(URL2); 314 315 mCallback.onStartedCallback.assertCalledWith(curStartedCount, URL2); 316 mCallback.onReadyToCommitCallback.assertCalledWith(curCommittedCount, URL2); 317 mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL2); 318 mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); 319 assertEquals(mCallback.onCompletedCallback.getHttpStatusCode(), 200); 320 } 321 322 @MinWebLayerVersion(85) 323 @Test 324 @SmallTest testOldPageNoLongerRendered()325 public void testOldPageNoLongerRendered() throws Exception { 326 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 327 setNavigationCallback(activity); 328 329 int renderedCount = mCallback.onOldPageNoLongerRenderedCallback.getCallCount(); 330 mActivityTestRule.navigateAndWait(URL2); 331 mCallback.onOldPageNoLongerRenderedCallback.waitForCallback(renderedCount); 332 assertEquals(Uri.parse(URL2), mCallback.onOldPageNoLongerRenderedCallback.getUri()); 333 } 334 335 @Test 336 @SmallTest testLoadStateUpdates()337 public void testLoadStateUpdates() throws Exception { 338 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 339 setNavigationCallback(activity); 340 mActivityTestRule.navigateAndWait(URL1); 341 342 /* Wait until the NavigationCallback is notified of load completion. */ 343 mCallback.loadStateChangedCallback.waitUntilValueObserved("false false"); 344 mCallback.loadProgressChangedCallback.waitUntilValueObserved("load complete"); 345 346 /* Verify that the NavigationCallback was notified of load progress /before/ load 347 * completion. 348 */ 349 int finishStateIndex = 350 mCallback.loadStateChangedCallback.getObservedValues().indexOf("false false"); 351 int finishProgressIndex = 352 mCallback.loadProgressChangedCallback.getObservedValues().indexOf("load complete"); 353 int startStateIndex = 354 mCallback.loadStateChangedCallback.getObservedValues().lastIndexOf("true true"); 355 int startProgressIndex = 356 mCallback.loadProgressChangedCallback.getObservedValues().lastIndexOf( 357 "load started"); 358 359 assertNotEquals(startStateIndex, -1); 360 assertNotEquals(startProgressIndex, -1); 361 assertNotEquals(finishStateIndex, -1); 362 assertNotEquals(finishProgressIndex, -1); 363 364 assertTrue(startStateIndex < finishStateIndex); 365 assertTrue(startProgressIndex < finishProgressIndex); 366 } 367 368 @Test 369 @SmallTest 370 public void testReplace() throws Exception { 371 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 372 setNavigationCallback(activity); 373 374 final NavigateParams params = 375 new NavigateParams.Builder().setShouldReplaceCurrentEntry(true).build(); 376 navigateAndWaitForCompletion(URL2, 377 () 378 -> activity.getTab().getNavigationController().navigate( 379 Uri.parse(URL2), params)); 380 runOnUiThreadBlocking(() -> { 381 NavigationController navigationController = activity.getTab().getNavigationController(); 382 assertFalse(navigationController.canGoForward()); 383 assertFalse(navigationController.canGoBack()); 384 assertEquals(1, navigationController.getNavigationListSize()); 385 }); 386 387 // Verify getter works as expected. 388 assertTrue(params.getShouldReplaceCurrentEntry()); 389 390 // Verify that a default NavigateParams does not replace. 391 final NavigateParams params2 = new NavigateParams(); 392 navigateAndWaitForCompletion(URL3, 393 () 394 -> activity.getTab().getNavigationController().navigate( 395 Uri.parse(URL3), params2)); 396 runOnUiThreadBlocking(() -> { 397 NavigationController navigationController = activity.getTab().getNavigationController(); 398 assertFalse(navigationController.canGoForward()); 399 assertTrue(navigationController.canGoBack()); 400 assertEquals(2, navigationController.getNavigationListSize()); 401 }); 402 } 403 404 @Test 405 @SmallTest testGoBackAndForward()406 public void testGoBackAndForward() throws Exception { 407 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 408 setNavigationCallback(activity); 409 410 mActivityTestRule.navigateAndWait(URL2); 411 mActivityTestRule.navigateAndWait(URL3); 412 413 NavigationController navigationController = 414 runOnUiThreadBlocking(() -> activity.getTab().getNavigationController()); 415 416 navigateAndWaitForCompletion(URL2, () -> { 417 assertTrue(navigationController.canGoBack()); 418 navigationController.goBack(); 419 }); 420 421 navigateAndWaitForCompletion(URL1, () -> { 422 assertTrue(navigationController.canGoBack()); 423 navigationController.goBack(); 424 }); 425 426 navigateAndWaitForCompletion(URL2, () -> { 427 assertFalse(navigationController.canGoBack()); 428 assertTrue(navigationController.canGoForward()); 429 navigationController.goForward(); 430 }); 431 432 navigateAndWaitForCompletion(URL3, () -> { 433 assertTrue(navigationController.canGoForward()); 434 navigationController.goForward(); 435 }); 436 437 runOnUiThreadBlocking(() -> { assertFalse(navigationController.canGoForward()); }); 438 } 439 440 @Test 441 @SmallTest testGoToIndex()442 public void testGoToIndex() throws Exception { 443 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 444 setNavigationCallback(activity); 445 446 mActivityTestRule.navigateAndWait(URL2); 447 mActivityTestRule.navigateAndWait(URL3); 448 mActivityTestRule.navigateAndWait(URL4); 449 450 // Navigate back to the 2nd url. 451 assertEquals(URL2, goToIndexAndReturnUrl(activity.getTab(), 1)); 452 453 // Navigate forwards to the 4th url. 454 assertEquals(URL4, goToIndexAndReturnUrl(activity.getTab(), 3)); 455 } 456 457 @Test 458 @SmallTest testGetNavigationEntryTitle()459 public void testGetNavigationEntryTitle() throws Exception { 460 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl( 461 "data:text/html,<head><title>Page A</title></head>"); 462 setNavigationCallback(activity); 463 464 mActivityTestRule.navigateAndWait("data:text/html,<head><title>Page B</title></head>"); 465 mActivityTestRule.navigateAndWait("data:text/html,<head><title>Page C</title></head>"); 466 467 runOnUiThreadBlocking(() -> { 468 NavigationController navigationController = activity.getTab().getNavigationController(); 469 assertEquals("Page A", navigationController.getNavigationEntryTitle(0)); 470 assertEquals("Page B", navigationController.getNavigationEntryTitle(1)); 471 assertEquals("Page C", navigationController.getNavigationEntryTitle(2)); 472 }); 473 } 474 475 @Test 476 @SmallTest testSameDocument()477 public void testSameDocument() throws Exception { 478 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 479 setNavigationCallback(activity); 480 481 int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); 482 483 mActivityTestRule.executeScriptSync( 484 "history.pushState(null, '', '#bar');", true /* useSeparateIsolate */); 485 486 mCallback.onCompletedCallback.assertCalledWith( 487 curCompletedCount, "data:text,foo#bar", true); 488 } 489 490 @Test 491 @SmallTest testReload()492 public void testReload() throws Exception { 493 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 494 setNavigationCallback(activity); 495 496 navigateAndWaitForCompletion( 497 URL1, () -> { activity.getTab().getNavigationController().reload(); }); 498 } 499 500 @Test 501 @SmallTest testStop()502 public void testStop() throws Exception { 503 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 504 setNavigationCallback(activity); 505 506 int curFailedCount = mCallback.onFailedCallback.getCallCount(); 507 508 runOnUiThreadBlocking(() -> { 509 NavigationController navigationController = activity.getTab().getNavigationController(); 510 navigationController.registerNavigationCallback(new NavigationCallback() { 511 @Override 512 public void onNavigationStarted(Navigation navigation) { 513 navigationController.stop(); 514 } 515 }); 516 navigationController.navigate(Uri.parse(URL2)); 517 }); 518 519 mCallback.onFailedCallback.assertCalledWith(curFailedCount, URL2); 520 } 521 522 @Test 523 @SmallTest testRedirect()524 public void testRedirect() throws Exception { 525 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 526 setNavigationCallback(activity); 527 528 int curRedirectedCount = mCallback.onRedirectedCallback.getCallCount(); 529 530 String finalUrl = mActivityTestRule.getTestServer().getURL("/echo"); 531 String url = mActivityTestRule.getTestServer().getURL("/server-redirect?" + finalUrl); 532 navigateAndWaitForCompletion(finalUrl, 533 () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url)); }); 534 535 mCallback.onRedirectedCallback.assertCalledWith( 536 curRedirectedCount, Arrays.asList(Uri.parse(url), Uri.parse(finalUrl))); 537 } 538 539 @Test 540 @SmallTest testNavigationList()541 public void testNavigationList() throws Exception { 542 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 543 setNavigationCallback(activity); 544 545 mActivityTestRule.navigateAndWait(URL2); 546 mActivityTestRule.navigateAndWait(URL3); 547 548 NavigationController navigationController = 549 runOnUiThreadBlocking(() -> activity.getTab().getNavigationController()); 550 551 runOnUiThreadBlocking(() -> { 552 assertEquals(3, navigationController.getNavigationListSize()); 553 assertEquals(2, navigationController.getNavigationListCurrentIndex()); 554 assertEquals(URL1, navigationController.getNavigationEntryDisplayUri(0).toString()); 555 assertEquals(URL2, navigationController.getNavigationEntryDisplayUri(1).toString()); 556 assertEquals(URL3, navigationController.getNavigationEntryDisplayUri(2).toString()); 557 }); 558 559 navigateAndWaitForCompletion(URL2, () -> { navigationController.goBack(); }); 560 561 runOnUiThreadBlocking(() -> { 562 assertEquals(3, navigationController.getNavigationListSize()); 563 assertEquals(1, navigationController.getNavigationListCurrentIndex()); 564 }); 565 } 566 567 @Test 568 @SmallTest testLoadError()569 public void testLoadError() throws Exception { 570 String url = mActivityTestRule.getTestDataURL("non_existent.html"); 571 572 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); 573 setNavigationCallback(activity); 574 575 int curFailedCount = mCallback.onFailedCallback.getCallCount(); 576 577 // navigateAndWait() expects a success code, so it won't work here. 578 runOnUiThreadBlocking( 579 () -> { activity.getTab().getNavigationController().navigate(Uri.parse(url)); }); 580 581 mCallback.onFailedCallback.assertCalledWith( 582 curFailedCount, url, LoadError.HTTP_CLIENT_ERROR); 583 assertEquals(mCallback.onFailedCallback.getHttpStatusCode(), 404); 584 assertEquals(mCallback.onFailedCallback.getNavigationState(), NavigationState.FAILED); 585 } 586 587 @Test 588 @SmallTest testRepostConfirmation()589 public void testRepostConfirmation() throws Exception { 590 // Load a page with a form. 591 InstrumentationActivity activity = 592 mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html")); 593 assertNotNull(activity); 594 setNavigationCallback(activity); 595 596 // Touch the page; this should submit the form. 597 int currentCallCount = mCallback.onCompletedCallback.getCallCount(); 598 EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); 599 String targetUrl = mActivityTestRule.getTestDataURL("simple_page.html"); 600 mCallback.onCompletedCallback.assertCalledWith(currentCallCount, targetUrl); 601 602 // Make sure a tab modal shows after we attempt a reload. 603 Boolean isTabModalShowingResult[] = new Boolean[1]; 604 CallbackHelper callbackHelper = new CallbackHelper(); 605 runOnUiThreadBlocking(() -> { 606 Tab tab = activity.getTab(); 607 TabCallback callback = new TabCallback() { 608 @Override 609 public void onTabModalStateChanged(boolean isTabModalShowing) { 610 isTabModalShowingResult[0] = isTabModalShowing; 611 callbackHelper.notifyCalled(); 612 } 613 }; 614 tab.registerTabCallback(callback); 615 tab.getNavigationController().reload(); 616 }); 617 618 callbackHelper.waitForFirst(); 619 assertTrue(isTabModalShowingResult[0]); 620 } 621 setNavigationCallback(InstrumentationActivity activity)622 private void setNavigationCallback(InstrumentationActivity activity) { 623 runOnUiThreadBlocking( 624 () 625 -> activity.getTab().getNavigationController().registerNavigationCallback( 626 mCallback)); 627 } 628 registerNavigationCallback(NavigationCallback callback)629 private void registerNavigationCallback(NavigationCallback callback) { 630 runOnUiThreadBlocking(() 631 -> mActivityTestRule.getActivity() 632 .getTab() 633 .getNavigationController() 634 .registerNavigationCallback(callback)); 635 } 636 unregisterNavigationCallback(NavigationCallback callback)637 private void unregisterNavigationCallback(NavigationCallback callback) { 638 runOnUiThreadBlocking(() 639 -> mActivityTestRule.getActivity() 640 .getTab() 641 .getNavigationController() 642 .unregisterNavigationCallback(callback)); 643 } 644 navigateAndWaitForCompletion(String expectedUrl, Runnable navigateRunnable)645 private void navigateAndWaitForCompletion(String expectedUrl, Runnable navigateRunnable) 646 throws Exception { 647 int currentCallCount = mCallback.onCompletedCallback.getCallCount(); 648 runOnUiThreadBlocking(navigateRunnable); 649 mCallback.onCompletedCallback.assertCalledWith(currentCallCount, expectedUrl); 650 } 651 goToIndexAndReturnUrl(Tab tab, int index)652 private String goToIndexAndReturnUrl(Tab tab, int index) throws Exception { 653 NavigationController navigationController = 654 runOnUiThreadBlocking(() -> tab.getNavigationController()); 655 656 final BoundedCountDownLatch navigationComplete = new BoundedCountDownLatch(1); 657 final AtomicReference<String> navigationUrl = new AtomicReference<String>(); 658 NavigationCallback navigationCallback = new NavigationCallback() { 659 @Override 660 public void onNavigationCompleted(Navigation navigation) { 661 navigationComplete.countDown(); 662 navigationUrl.set(navigation.getUri().toString()); 663 } 664 }; 665 666 runOnUiThreadBlocking(() -> { 667 navigationController.registerNavigationCallback(navigationCallback); 668 navigationController.goToIndex(index); 669 }); 670 671 navigationComplete.timedAwait(); 672 673 runOnUiThreadBlocking( 674 () -> { navigationController.unregisterNavigationCallback(navigationCallback); }); 675 676 return navigationUrl.get(); 677 } 678 679 @Test 680 @SmallTest testStopFromOnNavigationStarted()681 public void testStopFromOnNavigationStarted() throws Exception { 682 final InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 683 final BoundedCountDownLatch doneLatch = new BoundedCountDownLatch(1); 684 NavigationCallback navigationCallback = new NavigationCallback() { 685 @Override 686 public void onNavigationStarted(Navigation navigation) { 687 activity.getTab().getNavigationController().stop(); 688 doneLatch.countDown(); 689 } 690 }; 691 runOnUiThreadBlocking(() -> { 692 NavigationController controller = activity.getTab().getNavigationController(); 693 controller.registerNavigationCallback(navigationCallback); 694 controller.navigate(Uri.parse(URL1)); 695 }); 696 doneLatch.timedAwait(); 697 } 698 699 // NavigationCallback implementation that sets a header in either start or redirect. 700 private static final class HeaderSetter extends NavigationCallback { 701 private final String mName; 702 private final String mValue; 703 private final boolean mInStart; 704 public boolean mGotIllegalArgumentException; 705 HeaderSetter(String name, String value, boolean inStart)706 HeaderSetter(String name, String value, boolean inStart) { 707 mName = name; 708 mValue = value; 709 mInStart = inStart; 710 } 711 712 @Override onNavigationStarted(Navigation navigation)713 public void onNavigationStarted(Navigation navigation) { 714 if (mInStart) applyHeader(navigation); 715 } 716 717 @Override onNavigationRedirected(Navigation navigation)718 public void onNavigationRedirected(Navigation navigation) { 719 if (!mInStart) applyHeader(navigation); 720 } 721 applyHeader(Navigation navigation)722 private void applyHeader(Navigation navigation) { 723 try { 724 navigation.setRequestHeader(mName, mValue); 725 } catch (IllegalArgumentException e) { 726 mGotIllegalArgumentException = true; 727 } 728 } 729 } 730 731 @Test 732 @SmallTest testSetRequestHeaderInStart()733 public void testSetRequestHeaderInStart() throws Exception { 734 TestWebServer testServer = TestWebServer.start(); 735 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 736 String headerName = "header"; 737 String headerValue = "value"; 738 HeaderSetter setter = new HeaderSetter(headerName, headerValue, true); 739 registerNavigationCallback(setter); 740 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 741 mActivityTestRule.navigateAndWait(url); 742 assertFalse(setter.mGotIllegalArgumentException); 743 assertEquals(headerValue, testServer.getLastRequest("/ok.html").headerValue(headerName)); 744 } 745 746 @Test 747 @SmallTest testSetRequestHeaderInRedirect()748 public void testSetRequestHeaderInRedirect() throws Exception { 749 TestWebServer testServer = TestWebServer.start(); 750 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 751 String headerName = "header"; 752 String headerValue = "value"; 753 HeaderSetter setter = new HeaderSetter(headerName, headerValue, false); 754 registerNavigationCallback(setter); 755 // The destination of the redirect. 756 String finalUrl = testServer.setResponse("/ok.html", "<html>ok</html>", null); 757 // The url that redirects to |finalUrl|. 758 String redirectingUrl = testServer.setRedirect("/redirect.html", finalUrl); 759 Tab tab = mActivityTestRule.getActivity().getTab(); 760 NavigationWaiter waiter = new NavigationWaiter(finalUrl, tab, false, false); 761 TestThreadUtils.runOnUiThreadBlocking( 762 () -> { tab.getNavigationController().navigate(Uri.parse(redirectingUrl)); }); 763 waiter.waitForNavigation(); 764 assertFalse(setter.mGotIllegalArgumentException); 765 assertEquals(headerValue, testServer.getLastRequest("/ok.html").headerValue(headerName)); 766 } 767 768 @Test 769 @SmallTest testSetRequestHeaderThrowsExceptionInCompleted()770 public void testSetRequestHeaderThrowsExceptionInCompleted() throws Exception { 771 mActivityTestRule.launchShellWithUrl(null); 772 boolean gotCompleted[] = new boolean[1]; 773 NavigationCallback navigationCallback = new NavigationCallback() { 774 @Override 775 public void onNavigationCompleted(Navigation navigation) { 776 gotCompleted[0] = true; 777 boolean gotException = false; 778 try { 779 navigation.setRequestHeader("name", "value"); 780 } catch (IllegalStateException e) { 781 gotException = true; 782 } 783 assertTrue(gotException); 784 } 785 }; 786 registerNavigationCallback(navigationCallback); 787 mActivityTestRule.navigateAndWait(URL1); 788 assertTrue(gotCompleted[0]); 789 } 790 791 @Test 792 @SmallTest testSetRequestHeaderThrowsExceptionWithInvalidValue()793 public void testSetRequestHeaderThrowsExceptionWithInvalidValue() throws Exception { 794 mActivityTestRule.launchShellWithUrl(null); 795 HeaderSetter setter = new HeaderSetter("name", "\0", true); 796 registerNavigationCallback(setter); 797 mActivityTestRule.navigateAndWait(URL1); 798 assertTrue(setter.mGotIllegalArgumentException); 799 } 800 801 // NavigationCallback implementation that sets the user-agent string in onNavigationStarted(). 802 private static final class UserAgentSetter extends NavigationCallback { 803 private final String mValue; 804 public boolean mGotIllegalStateException; 805 UserAgentSetter(String value)806 UserAgentSetter(String value) { 807 mValue = value; 808 } 809 810 @Override onNavigationStarted(Navigation navigation)811 public void onNavigationStarted(Navigation navigation) { 812 try { 813 navigation.setUserAgentString(mValue); 814 } catch (IllegalStateException e) { 815 mGotIllegalStateException = true; 816 } 817 } 818 } 819 820 @Test 821 @SmallTest 822 @MinWebLayerVersion(84) testSetUserAgentString()823 public void testSetUserAgentString() throws Exception { 824 TestWebServer testServer = TestWebServer.start(); 825 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 826 String customUserAgent = "custom-ua"; 827 UserAgentSetter setter = new UserAgentSetter(customUserAgent); 828 registerNavigationCallback(setter); 829 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 830 mActivityTestRule.navigateAndWait(url); 831 String actualUserAgent = testServer.getLastRequest("/ok.html").headerValue("User-Agent"); 832 assertEquals(customUserAgent, actualUserAgent); 833 } 834 835 @Test 836 @SmallTest 837 @MinWebLayerVersion(88) testCantUsePerNavigationAndDesktopMode()838 public void testCantUsePerNavigationAndDesktopMode() throws Exception { 839 TestWebServer testServer = TestWebServer.start(); 840 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 841 UserAgentSetter setter = new UserAgentSetter("foo"); 842 registerNavigationCallback(setter); 843 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 844 runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); 845 mActivityTestRule.navigateAndWait(url); 846 assertTrue(setter.mGotIllegalStateException); 847 } 848 849 @Test 850 @SmallTest 851 @MinWebLayerVersion(88) testDesktopMode()852 public void testDesktopMode() throws Exception { 853 TestWebServer testServer = TestWebServer.start(); 854 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); 855 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 856 runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); 857 mActivityTestRule.navigateAndWait(url); 858 String actualUserAgent = testServer.getLastRequest("/ok.html").headerValue("User-Agent"); 859 assertFalse(actualUserAgent.contains("Android")); 860 } 861 862 @Test 863 @SmallTest 864 @MinWebLayerVersion(88) testDesktopModeSticks()865 public void testDesktopModeSticks() throws Exception { 866 TestWebServer testServer = TestWebServer.start(); 867 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl("about:blank"); 868 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 869 String url2 = testServer.setResponse("/ok2.html", "<html>ok</html>", null); 870 runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); 871 mActivityTestRule.navigateAndWait(url); 872 mActivityTestRule.navigateAndWait(url2); 873 String actualUserAgent = testServer.getLastRequest("/ok2.html").headerValue("User-Agent"); 874 assertFalse(actualUserAgent.contains("Android")); 875 } 876 877 @Test 878 @SmallTest 879 @MinWebLayerVersion(88) testDesktopModeGetter()880 public void testDesktopModeGetter() throws Exception { 881 TestWebServer testServer = TestWebServer.start(); 882 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 883 setNavigationCallback(activity); 884 885 UserAgentSetter setter = new UserAgentSetter("foo"); 886 registerNavigationCallback(setter); 887 mActivityTestRule.navigateAndWait(URL1); 888 unregisterNavigationCallback(setter); 889 runOnUiThreadBlocking( 890 () -> { assertFalse(activity.getTab().isDesktopUserAgentEnabled()); }); 891 892 runOnUiThreadBlocking(() -> { activity.getTab().setDesktopUserAgentEnabled(true); }); 893 mActivityTestRule.navigateAndWait(URL2); 894 runOnUiThreadBlocking(() -> { assertTrue(activity.getTab().isDesktopUserAgentEnabled()); }); 895 896 navigateAndWaitForCompletion( 897 URL1, () -> activity.getTab().getNavigationController().goBack()); 898 runOnUiThreadBlocking( 899 () -> { assertFalse(activity.getTab().isDesktopUserAgentEnabled()); }); 900 } 901 902 @Test 903 @SmallTest 904 @MinWebLayerVersion(85) testSkippedNavigationEntry()905 public void testSkippedNavigationEntry() throws Exception { 906 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 907 setNavigationCallback(activity); 908 909 int curCompletedCount = mCallback.onCompletedCallback.getCallCount(); 910 mActivityTestRule.executeScriptSync( 911 "history.pushState(null, '', '#foo');", true /* useSeparateIsolate */); 912 mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL1 + "#foo", true); 913 914 curCompletedCount = mCallback.onCompletedCallback.getCallCount(); 915 mActivityTestRule.executeScriptSync( 916 "history.pushState(null, '', '#bar');", true /* useSeparateIsolate */); 917 mCallback.onCompletedCallback.assertCalledWith(curCompletedCount, URL1 + "#bar", true); 918 919 runOnUiThreadBlocking(() -> { 920 NavigationController navigationController = activity.getTab().getNavigationController(); 921 int currentIndex = navigationController.getNavigationListCurrentIndex(); 922 // Should skip the two previous same document entries, but not the most recent. 923 assertFalse(navigationController.isNavigationEntrySkippable(currentIndex)); 924 assertTrue(navigationController.isNavigationEntrySkippable(currentIndex - 1)); 925 assertTrue(navigationController.isNavigationEntrySkippable(currentIndex - 2)); 926 }); 927 } 928 929 @Test 930 @SmallTest 931 @MinWebLayerVersion(85) testIndexOutOfBounds()932 public void testIndexOutOfBounds() throws Exception { 933 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 934 runOnUiThreadBlocking(() -> { 935 NavigationController controller = activity.getTab().getNavigationController(); 936 assertIndexOutOfBoundsException(() -> controller.goBack()); 937 assertIndexOutOfBoundsException(() -> controller.goForward()); 938 assertIndexOutOfBoundsException(() -> controller.goToIndex(10)); 939 assertIndexOutOfBoundsException(() -> controller.getNavigationEntryDisplayUri(10)); 940 assertIndexOutOfBoundsException(() -> controller.getNavigationEntryTitle(10)); 941 assertIndexOutOfBoundsException(() -> controller.isNavigationEntrySkippable(10)); 942 }); 943 } 944 assertIndexOutOfBoundsException(Runnable runnable)945 private static void assertIndexOutOfBoundsException(Runnable runnable) { 946 try { 947 runnable.run(); 948 } catch (IndexOutOfBoundsException e) { 949 // Expected exception. 950 return; 951 } 952 Assert.fail("Expected IndexOutOfBoundsException."); 953 } 954 955 @Test 956 @SmallTest 957 @MinWebLayerVersion(86) testPageInitiated()958 public void testPageInitiated() throws Exception { 959 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 960 setNavigationCallback(activity); 961 String initialUrl = mActivityTestRule.getTestDataURL("simple_page4.html"); 962 mActivityTestRule.navigateAndWait(initialUrl); 963 String refreshUrl = mActivityTestRule.getTestDataURL("simple_page.html"); 964 mCallback.onCompletedCallback.assertCalledWith( 965 mCallback.onCompletedCallback.getCallCount(), refreshUrl); 966 assertTrue(mCallback.onCompletedCallback.isPageInitiated()); 967 } 968 969 @Test 970 @SmallTest 971 @MinWebLayerVersion(86) testPageInitiatedFromClient()972 public void testPageInitiatedFromClient() throws Exception { 973 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 974 setNavigationCallback(activity); 975 mActivityTestRule.navigateAndWait(URL2); 976 assertFalse(mCallback.onStartedCallback.isPageInitiated()); 977 } 978 979 // Verifies the following sequence doesn't crash: 980 // 1. create a new background tab. 981 // 2. show modal dialog. 982 // 3. destroy tab with modal dialog. 983 // 4. switch to background tab created in step 1. 984 // This is a regression test for https://crbug.com/1121388. 985 @Test 986 @SmallTest 987 @MinWebLayerVersion(85) testDestroyTabWithModalDialog()988 public void testDestroyTabWithModalDialog() throws Exception { 989 // Load a page with a form. 990 InstrumentationActivity activity = 991 mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html")); 992 assertNotNull(activity); 993 setNavigationCallback(activity); 994 995 // Touch the page; this should submit the form. 996 int currentCallCount = mCallback.onCompletedCallback.getCallCount(); 997 EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView()); 998 String targetUrl = mActivityTestRule.getTestDataURL("simple_page.html"); 999 mCallback.onCompletedCallback.assertCalledWith(currentCallCount, targetUrl); 1000 1001 Tab secondTab = runOnUiThreadBlocking(() -> activity.getTab().getBrowser().createTab()); 1002 // Make sure a tab modal shows after we attempt a reload. 1003 Boolean isTabModalShowingResult[] = new Boolean[1]; 1004 CallbackHelper callbackHelper = new CallbackHelper(); 1005 runOnUiThreadBlocking(() -> { 1006 Tab tab = activity.getTab(); 1007 Browser browser = tab.getBrowser(); 1008 TabCallback callback = new TabCallback() { 1009 @Override 1010 public void onTabModalStateChanged(boolean isTabModalShowing) { 1011 tab.unregisterTabCallback(this); 1012 isTabModalShowingResult[0] = isTabModalShowing; 1013 callbackHelper.notifyCalled(); 1014 } 1015 }; 1016 tab.registerTabCallback(callback); 1017 1018 browser.registerTabListCallback(new TabListCallback() { 1019 @Override 1020 public void onTabRemoved(Tab tab) { 1021 browser.unregisterTabListCallback(this); 1022 browser.setActiveTab(secondTab); 1023 } 1024 }); 1025 tab.getNavigationController().reload(); 1026 }); 1027 1028 callbackHelper.waitForFirst(); 1029 runOnUiThreadBlocking(() -> { 1030 Tab tab = activity.getTab(); 1031 tab.getBrowser().destroyTab(tab); 1032 }); 1033 } 1034 1035 /** 1036 * This test verifies calling destroyTab() from within onNavigationFailed doesn't crash. 1037 */ 1038 @Test 1039 @SmallTest 1040 @MinWebLayerVersion(86) testDestroyTabInNavigationFailed()1041 public void testDestroyTabInNavigationFailed() throws Throwable { 1042 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 1043 CallbackHelper callbackHelper = new CallbackHelper(); 1044 runOnUiThreadBlocking(() -> { 1045 NavigationController navigationController = activity.getTab().getNavigationController(); 1046 navigationController.registerNavigationCallback(new NavigationCallback() { 1047 @Override 1048 public void onNavigationFailed(Navigation navigation) { 1049 navigationController.unregisterNavigationCallback(this); 1050 Tab tab = activity.getTab(); 1051 tab.getBrowser().destroyTab(tab); 1052 callbackHelper.notifyCalled(); 1053 } 1054 }); 1055 }); 1056 TestThreadUtils.runOnUiThreadBlocking(() -> { 1057 activity.getTab().getNavigationController().navigate( 1058 Uri.parse("http://localhost:7/non_existent")); 1059 }); 1060 callbackHelper.waitForFirst(); 1061 } 1062 navigateToStream(InstrumentationActivity activity, String mimeType, String cacheControl)1063 private void navigateToStream(InstrumentationActivity activity, String mimeType, 1064 String cacheControl) throws Exception { 1065 int curOnFirstContentfulPaintCount = 1066 mCallback.onFirstContentfulPaintCallback.getCallCount(); 1067 InputStream stream = new ByteArrayInputStream(STREAM_HTML.getBytes(StandardCharsets.UTF_8)); 1068 WebResourceResponse response = new WebResourceResponse(mimeType, "UTF-8", stream); 1069 if (cacheControl != null) { 1070 Map<String, String> headers = new HashMap<>(); 1071 headers.put("Cache-Control", cacheControl); 1072 response.setResponseHeaders(headers); 1073 } 1074 1075 final NavigateParams params = new NavigateParams.Builder().setResponse(response).build(); 1076 navigateAndWaitForCompletion(STREAM_URL, 1077 () 1078 -> activity.getTab().getNavigationController().navigate( 1079 Uri.parse(STREAM_URL), params)); 1080 mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); 1081 } 1082 assertStreamContent()1083 private void assertStreamContent() throws Exception { 1084 assertEquals(STREAM_INNER_BODY, 1085 mActivityTestRule.executeScriptAndExtractString("document.body.innerText")); 1086 } 1087 1088 @Test 1089 @SmallTest 1090 @MinWebLayerVersion(87) testWebResponse()1091 public void testWebResponse() throws Exception { 1092 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1093 // The code asserts that when InputStreams are used that the stock URL bar is not visible. 1094 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1095 setNavigationCallback(activity); 1096 1097 navigateToStream(activity, "text/html", null); 1098 assertStreamContent(); 1099 } 1100 1101 @Test 1102 @SmallTest 1103 @MinWebLayerVersion(87) testWebResponseMimeSniff()1104 public void testWebResponseMimeSniff() throws Exception { 1105 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1106 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1107 setNavigationCallback(activity); 1108 1109 navigateToStream(activity, "", null); 1110 assertStreamContent(); 1111 } 1112 1113 @Test 1114 @SmallTest 1115 @MinWebLayerVersion(87) testWebResponseNoCacheControl()1116 public void testWebResponseNoCacheControl() throws Exception { 1117 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1118 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1119 setNavigationCallback(activity); 1120 1121 navigateToStream(activity, "text/html", null); 1122 1123 mActivityTestRule.navigateAndWait(URL1); 1124 1125 int curFailedCount = mCallback.onFailedCallback.getCallCount(); 1126 runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); 1127 mCallback.onFailedCallback.assertCalledWith( 1128 curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); 1129 } 1130 1131 @Test 1132 @SmallTest 1133 @MinWebLayerVersion(87) testWebResponseCached()1134 public void testWebResponseCached() throws Exception { 1135 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1136 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1137 setNavigationCallback(activity); 1138 1139 navigateToStream(activity, "text/html", "private, max-age=60"); 1140 1141 // Now check that the data can be reused from the cache if it had the correct headers. 1142 mActivityTestRule.navigateAndWait(URL1); 1143 int curOnFirstContentfulPaintCount = 1144 mCallback.onFirstContentfulPaintCallback.getCallCount(); 1145 navigateAndWaitForCompletion( 1146 STREAM_URL, () -> { activity.getTab().getNavigationController().goBack(); }); 1147 mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); 1148 assertStreamContent(); 1149 } 1150 1151 @Test 1152 @SmallTest 1153 @MinWebLayerVersion(87) testWebResponseCachedWithSniffedMimeType()1154 public void testWebResponseCachedWithSniffedMimeType() throws Exception { 1155 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1156 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1157 setNavigationCallback(activity); 1158 1159 navigateToStream(activity, "", "private, max-age=60"); 1160 1161 mActivityTestRule.navigateAndWait(URL1); 1162 1163 int curOnFirstContentfulPaintCount = 1164 mCallback.onFirstContentfulPaintCallback.getCallCount(); 1165 navigateAndWaitForCompletion( 1166 STREAM_URL, () -> { activity.getTab().getNavigationController().goBack(); }); 1167 mCallback.onFirstContentfulPaintCallback.waitForCallback(curOnFirstContentfulPaintCount); 1168 assertStreamContent(); 1169 } 1170 1171 @Test 1172 @SmallTest 1173 @MinWebLayerVersion(87) testWebResponseNoStore()1174 public void testWebResponseNoStore() throws Exception { 1175 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1176 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1177 setNavigationCallback(activity); 1178 1179 navigateToStream(activity, "text/html", "no-store"); 1180 1181 mActivityTestRule.navigateAndWait(URL1); 1182 1183 int curFailedCount = mCallback.onFailedCallback.getCallCount(); 1184 runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); 1185 mCallback.onFailedCallback.assertCalledWith( 1186 curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); 1187 } 1188 1189 @Test 1190 @SmallTest 1191 @MinWebLayerVersion(87) testWebResponseExpired()1192 public void testWebResponseExpired() throws Exception { 1193 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(URL1); 1194 TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); }); 1195 setNavigationCallback(activity); 1196 1197 navigateToStream(activity, "text/html", "private, max-age=2"); 1198 1199 Thread.sleep(5000); 1200 1201 mActivityTestRule.navigateAndWait(URL1); 1202 1203 int curFailedCount = mCallback.onFailedCallback.getCallCount(); 1204 runOnUiThreadBlocking(() -> { activity.getTab().getNavigationController().goBack(); }); 1205 mCallback.onFailedCallback.assertCalledWith( 1206 curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR); 1207 } 1208 1209 @MinWebLayerVersion(88) 1210 @Test 1211 @SmallTest testOnFirstContentfulPaintTiming()1212 public void testOnFirstContentfulPaintTiming() throws Exception { 1213 long activityStartTimeMs = SystemClock.uptimeMillis(); 1214 1215 TestWebServer testServer = TestWebServer.start(); 1216 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 1217 setNavigationCallback(activity); 1218 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 1219 1220 int count = mCallback.onFirstContentfulPaint2Callback.getCallCount(); 1221 mActivityTestRule.navigateAndWait(url); 1222 mCallback.onFirstContentfulPaint2Callback.waitForCallback(count); 1223 1224 long navigationStart = mCallback.onFirstContentfulPaint2Callback.getNavigationStartMillis(); 1225 long current = SystemClock.uptimeMillis(); 1226 Assert.assertTrue(navigationStart <= current); 1227 Assert.assertTrue(navigationStart >= activityStartTimeMs); 1228 1229 long firstContentfulPaint = 1230 mCallback.onFirstContentfulPaint2Callback.getFirstContentfulPaintMs(); 1231 Assert.assertTrue(firstContentfulPaint <= (current - navigationStart)); 1232 } 1233 1234 @MinWebLayerVersion(88) 1235 @Test 1236 @SmallTest testOnLargestContentfulPaintTiming()1237 public void testOnLargestContentfulPaintTiming() throws Exception { 1238 long activityStartTimeMs = SystemClock.uptimeMillis(); 1239 1240 TestWebServer testServer = TestWebServer.start(); 1241 InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null); 1242 setNavigationCallback(activity); 1243 String url = testServer.setResponse("/ok.html", "<html>ok</html>", null); 1244 1245 int count = mCallback.onLargestContentfulPaintCallback.getCallCount(); 1246 mActivityTestRule.navigateAndWait(url); 1247 1248 // Navigate to a new page, as metrics like LCP are only reported at the end of the page load 1249 // lifetime. 1250 mActivityTestRule.navigateAndWait("about:blank"); 1251 mCallback.onLargestContentfulPaintCallback.waitForCallback(count); 1252 1253 long navigationStart = 1254 mCallback.onLargestContentfulPaintCallback.getNavigationStartMillis(); 1255 long current = SystemClock.uptimeMillis(); 1256 Assert.assertTrue(navigationStart <= current); 1257 Assert.assertTrue(navigationStart >= activityStartTimeMs); 1258 1259 long largestContentfulPaint = 1260 mCallback.onLargestContentfulPaintCallback.getLargestContentfulPaintMs(); 1261 Assert.assertTrue(largestContentfulPaint <= (current - navigationStart)); 1262 } 1263 } 1264