Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.tabmodel; | 5 package org.chromium.chrome.browser.tabmodel; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | |
| 8 import android.os.Build; | |
| 7 import android.test.suitebuilder.annotation.MediumTest; | 9 import android.test.suitebuilder.annotation.MediumTest; |
| 8 | 10 |
| 11 import org.chromium.base.ApplicationStatus; | |
| 9 import org.chromium.base.ThreadUtils; | 12 import org.chromium.base.ThreadUtils; |
| 10 import org.chromium.base.test.util.FlakyTest; | 13 import org.chromium.base.test.util.FlakyTest; |
| 14 import org.chromium.base.test.util.MinAndroidSdkLevel; | |
| 11 import org.chromium.base.test.util.Restriction; | 15 import org.chromium.base.test.util.Restriction; |
| 16 import org.chromium.base.test.util.UrlUtils; | |
| 17 import org.chromium.chrome.browser.ChromeTabbedActivity; | |
| 18 import org.chromium.chrome.browser.ChromeTabbedActivity2; | |
| 19 import org.chromium.chrome.browser.multiwindow.MultiWindowUtilsTest; | |
| 20 import org.chromium.chrome.browser.tab.EmptyTabObserver; | |
| 12 import org.chromium.chrome.browser.tab.Tab; | 21 import org.chromium.chrome.browser.tab.Tab; |
| 13 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; | 22 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
| 14 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; | 23 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
| 15 import org.chromium.chrome.test.ChromeTabbedActivityTestBase; | 24 import org.chromium.chrome.test.ChromeTabbedActivityTestBase; |
| 16 import org.chromium.chrome.test.util.ChromeRestriction; | 25 import org.chromium.chrome.test.util.ChromeRestriction; |
| 17 import org.chromium.content.browser.test.util.CallbackHelper; | 26 import org.chromium.content.browser.test.util.CallbackHelper; |
| 27 import org.chromium.content.browser.test.util.Criteria; | |
| 28 import org.chromium.content.browser.test.util.CriteriaHelper; | |
| 18 import org.chromium.content_public.browser.LoadUrlParams; | 29 import org.chromium.content_public.browser.LoadUrlParams; |
| 19 | 30 |
| 31 import java.util.concurrent.Callable; | |
| 20 import java.util.concurrent.TimeoutException; | 32 import java.util.concurrent.TimeoutException; |
| 21 | 33 |
| 22 /** | 34 /** |
| 23 * Tests undo and restoring of tabs in a {@link TabModel}. | 35 * Tests undo and restoring of tabs in a {@link TabModel}. |
| 24 */ | 36 */ |
| 25 public class UndoTabModelTest extends ChromeTabbedActivityTestBase { | 37 public class UndoTabModelTest extends ChromeTabbedActivityTestBase { |
| 26 private static final Tab[] EMPTY = new Tab[] { }; | 38 private static final Tab[] EMPTY = new Tab[] { }; |
| 39 private static final String TEST_URL_0 = UrlUtils.encodeHtmlDataUri("<html>t est_url_0.</html>"); | |
| 40 private static final String TEST_URL_1 = UrlUtils.encodeHtmlDataUri("<html>t est_url_1</html>"); | |
| 27 | 41 |
| 28 @Override | 42 @Override |
| 29 public void startMainActivity() throws InterruptedException { | 43 public void startMainActivity() throws InterruptedException { |
| 30 startMainActivityOnBlankPage(); | 44 startMainActivityOnBlankPage(); |
| 31 } | 45 } |
| 32 | 46 |
| 33 private void checkState( | 47 private void checkState( |
| 34 final TabModel model, final Tab[] tabsList, final Tab selectedTab, | 48 final TabModel model, final Tab[] tabsList, final Tab selectedTab, |
| 35 final Tab[] closingTabs, final Tab[] fullTabsList, | 49 final Tab[] closingTabs, final Tab[] fullTabsList, |
| 36 final Tab fullSelectedTab) { | 50 final Tab fullSelectedTab) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 private void createTabOnUiThread(final ChromeTabCreator tabCreator) { | 82 private void createTabOnUiThread(final ChromeTabCreator tabCreator) { |
| 69 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 83 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 70 @Override | 84 @Override |
| 71 public void run() { | 85 public void run() { |
| 72 tabCreator.createNewTab(new LoadUrlParams("about:blank"), | 86 tabCreator.createNewTab(new LoadUrlParams("about:blank"), |
| 73 TabLaunchType.FROM_CHROME_UI, null); | 87 TabLaunchType.FROM_CHROME_UI, null); |
| 74 } | 88 } |
| 75 }); | 89 }); |
| 76 } | 90 } |
| 77 | 91 |
| 92 private void createFullyLoadedTabOnUiThread(final ChromeTabbedActivity activ ity, | |
| 93 final String url) { | |
| 94 final CallbackHelper tabCallbackHelper = new CallbackHelper(); | |
| 95 final TabLoadedObserver observer = new TabLoadedObserver(tabCallbackHelp er); | |
| 96 | |
| 97 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
| 98 @Override | |
| 99 public void run() { | |
| 100 activity.getTabCreator(false).createNewTab(new LoadUrlParams(url ), | |
| 101 TabLaunchType.FROM_CHROME_UI, null).addObserver(observer ); | |
| 102 } | |
| 103 }); | |
| 104 | |
| 105 // Must wait for the page to be fully loaded. | |
| 106 try { | |
| 107 tabCallbackHelper.waitForCallback(0); | |
| 108 } catch (TimeoutException | InterruptedException e) { | |
| 109 fail("Failed to load the tab."); | |
| 110 } | |
| 111 } | |
| 112 | |
| 78 private void selectTabOnUiThread(final TabModel model, final Tab tab) { | 113 private void selectTabOnUiThread(final TabModel model, final Tab tab) { |
| 79 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 114 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 80 @Override | 115 @Override |
| 81 public void run() { | 116 public void run() { |
| 82 model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER); | 117 model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER); |
| 83 } | 118 } |
| 84 }); | 119 }); |
| 85 } | 120 } |
| 86 | 121 |
| 87 private void closeTabOnUiThread( | 122 private void closeTabOnUiThread( |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 314 }); | 349 }); |
| 315 | 350 |
| 316 for (int i = 0; i < selector.getModels().size(); i++) { | 351 for (int i = 0; i < selector.getModels().size(); i++) { |
| 317 TabList tabs = selector.getModelAt(i).getComprehensiveModel(); | 352 TabList tabs = selector.getModelAt(i).getComprehensiveModel(); |
| 318 for (int j = 0; j < tabs.getCount(); j++) { | 353 for (int j = 0; j < tabs.getCount(); j++) { |
| 319 assertFalse(tabs.isClosurePending(tabs.getTabAt(j).getId())); | 354 assertFalse(tabs.isClosurePending(tabs.getTabAt(j).getId())); |
| 320 } | 355 } |
| 321 } | 356 } |
| 322 } | 357 } |
| 323 | 358 |
| 359 private void openMostRecentlyClosedTabOnUiThread(final TabModelSelector sele ctor) { | |
| 360 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
| 361 @Override | |
| 362 public void run() { | |
| 363 selector.getCurrentModel().openMostRecentlyClosedTab(); | |
| 364 } | |
| 365 }); | |
| 366 } | |
| 367 | |
| 368 // Helper class that notifies when a page load is finished. | |
| 369 private static class TabLoadedObserver extends EmptyTabObserver { | |
| 370 private CallbackHelper mLoadedCallback; | |
| 371 | |
| 372 public TabLoadedObserver(CallbackHelper loadCallback) { | |
| 373 super(); | |
| 374 mLoadedCallback = loadCallback; | |
| 375 } | |
| 376 @Override | |
| 377 public void onPageLoadFinished(Tab tab) { | |
| 378 mLoadedCallback.notifyCalled(); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 // Helper class that notifies after the tab is closed, and a tab restore ser vice entry has been | |
| 383 // created in tab restore service. | |
| 384 private static class TabClosedObserver extends EmptyTabModelObserver { | |
| 385 private CallbackHelper mTabClosedCallback; | |
| 386 | |
| 387 public TabClosedObserver(CallbackHelper closedCallback) { | |
| 388 mTabClosedCallback = closedCallback; | |
| 389 } | |
| 390 | |
| 391 @Override | |
| 392 public void didCloseTab(int tabId, boolean incognito) { | |
| 393 mTabClosedCallback.notifyCalled(); | |
| 394 } | |
| 395 } | |
| 396 | |
| 324 /** | 397 /** |
| 325 * Test undo with a single tab with the following actions/expected states: | 398 * Test undo with a single tab with the following actions/expected states: |
| 326 * Action Model List Close List Compr ehensive List | 399 * Action Model List Close List Compr ehensive List |
| 327 * 1. Initial State [ 0s ] - [ 0s ] | 400 * 1. Initial State [ 0s ] - [ 0s ] |
| 328 * 2. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 401 * 2. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
| 329 * 3. CancelClose(0) [ 0s ] - [ 0s ] | 402 * 3. CancelClose(0) [ 0s ] - [ 0s ] |
| 330 * 4. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 403 * 4. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
| 331 * 5. CommitClose(0) - - - | 404 * 5. CommitClose(0) - - - |
| 332 * 6. CreateTab(0) [ 0s ] - [ 0s ] | 405 * 6. CreateTab(0) [ 0s ] - [ 0s ] |
| 333 * 7. CloseTab(0, allow undo) - [ 0 ] [ 0s ] | 406 * 7. CloseTab(0, allow undo) - [ 0 ] [ 0s ] |
| (...skipping 1069 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1403 closeTabOnUiThread(model, tab0, true); | 1476 closeTabOnUiThread(model, tab0, true); |
| 1404 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); | 1477 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); |
| 1405 | 1478 |
| 1406 // 3. | 1479 // 3. |
| 1407 saveStateOnUiThread(selector); | 1480 saveStateOnUiThread(selector); |
| 1408 fullList = new Tab[] { tab1 }; | 1481 fullList = new Tab[] { tab1 }; |
| 1409 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); | 1482 checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1); |
| 1410 assertTrue(tab0.isClosing()); | 1483 assertTrue(tab0.isClosing()); |
| 1411 assertFalse(tab0.isInitialized()); | 1484 assertFalse(tab0.isInitialized()); |
| 1412 } | 1485 } |
| 1486 | |
| 1487 /** | |
| 1488 * Test opening recently closed tabs using the rewound list in Java. | |
| 1489 * @throws InterruptedException | |
| 1490 */ | |
| 1491 @MediumTest | |
| 1492 public void testOpenRecentlyClosedTab() throws InterruptedException { | |
| 1493 TabModelSelector selector = getActivity().getTabModelSelector(); | |
| 1494 TabModel model = selector.getModel(false); | |
| 1495 ChromeTabCreator tabCreator = getActivity().getTabCreator(false); | |
| 1496 | |
| 1497 createTabOnUiThread(tabCreator); | |
| 1498 | |
| 1499 Tab tab0 = model.getTabAt(0); | |
| 1500 Tab tab1 = model.getTabAt(1); | |
| 1501 Tab[] allTabs = new Tab[]{tab0, tab1}; | |
| 1502 | |
| 1503 closeTabOnUiThread(model, tab1, true); | |
| 1504 checkState(model, new Tab[]{tab0}, tab0, new Tab[]{tab1}, allTabs, tab0) ; | |
| 1505 | |
| 1506 // Ensure tab recovery, ordering, and reuse of {@link Tab} objects in Ja va. | |
| 1507 openMostRecentlyClosedTabOnUiThread(selector); | |
| 1508 checkState(model, allTabs, tab0, EMPTY, allTabs, tab0); | |
| 1509 } | |
| 1510 | |
| 1511 /** | |
| 1512 * Test opening recently closed tab using native tab restore service. | |
| 1513 * @throws InterruptedException | |
| 1514 */ | |
| 1515 @MediumTest | |
| 1516 public void testOpenRecentlyClosedTabNative() throws InterruptedException { | |
| 1517 final TabModelSelector selector = getActivity().getTabModelSelector(); | |
| 1518 final TabModel model = selector.getModel(false); | |
| 1519 | |
| 1520 // Create new tab and attach observer to listen to loaded event. | |
| 1521 // Native can only successfully recover the tab after a page load has fi nished and | |
| 1522 // it has navigation history. | |
| 1523 createFullyLoadedTabOnUiThread(getActivity(), TEST_URL_0); | |
| 1524 | |
| 1525 // Close the tab, and commit pending closure. | |
| 1526 assertEquals(model.getCount(), 2); | |
| 1527 closeTabOnUiThread(model, model.getTabAt(1), false); | |
| 1528 assertEquals(1, model.getCount()); | |
| 1529 Tab[] tabs = new Tab[]{model.getTabAt(0)}; | |
| 1530 checkState(model, tabs, tabs[0], EMPTY, tabs, tabs[0]); | |
| 1531 | |
| 1532 // Recover the page. | |
| 1533 openMostRecentlyClosedTabOnUiThread(selector); | |
| 1534 | |
| 1535 assertEquals(2, model.getCount()); | |
| 1536 tabs = new Tab[]{model.getTabAt(0), model.getTabAt(1)}; | |
| 1537 assertEquals(TEST_URL_0, tabs[1].getUrl()); | |
| 1538 checkState(model, tabs, tabs[0], EMPTY, tabs, tabs[0]); | |
| 1539 } | |
| 1540 | |
| 1541 /** | |
| 1542 * Test opening recently closed tab when we have multiple windows. | |
| 1543 * | Action | Result | |
| 1544 * 1. Create second window. | | |
| 1545 * 2. Open tab in window 1. | | |
| 1546 * 3. Open tab in window 2. | | |
| 1547 * 4. Close tab in window 1. | | |
| 1548 * 5. Close tab in window 2. | | |
| 1549 * 6. Restore tab. | Tab restored in window 2. | |
| 1550 * 7. Restore tab. | Tab restored in window 1. | |
| 1551 * @throws InterruptedException | |
| 1552 */ | |
| 1553 @MediumTest | |
| 1554 @MinAndroidSdkLevel(24) | |
| 1555 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 1556 public void testOpenRecentlyClosedTabMultiWindow() throws InterruptedExcepti on { | |
| 1557 final ChromeTabbedActivity2 secondActivity = | |
| 1558 MultiWindowUtilsTest.createSecondChromeTabbedActivity(getActivit y()); | |
| 1559 | |
| 1560 // Wait for the second window to be fully initialized. | |
| 1561 CriteriaHelper.pollUiThread(new Criteria() { | |
| 1562 @Override | |
| 1563 public boolean isSatisfied() { | |
| 1564 return secondActivity.getTabModelSelector().isTabStateInitialize d(); | |
| 1565 } | |
| 1566 }); | |
| 1567 // First window context. | |
| 1568 final TabModelSelector firstSelector = getActivity().getTabModelSelector (); | |
| 1569 final TabModel firstModel = firstSelector.getModel(false); | |
| 1570 | |
| 1571 // Second window context. | |
| 1572 final TabModel secondModel = secondActivity.getTabModelSelector().getMod el(false); | |
| 1573 | |
| 1574 // Create tabs. | |
| 1575 createFullyLoadedTabOnUiThread(getActivity(), TEST_URL_0); | |
| 1576 createFullyLoadedTabOnUiThread(secondActivity, TEST_URL_1); | |
| 1577 | |
| 1578 assertEquals("Unexpected number of tabs in first window.", 2, firstModel .getCount()); | |
| 1579 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
| 1580 | |
| 1581 // Close one tab in the first window. | |
| 1582 closeTabOnUiThread(firstModel, firstModel.getTabAt(1), false); | |
| 1583 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
| 1584 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
| 1585 | |
| 1586 // Close one tab in the second window. | |
| 1587 closeTabOnUiThread(secondModel, secondModel.getTabAt(1), false); | |
| 1588 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
| 1589 assertEquals("Unexpected number of tabs in second window.", 1, secondMod el.getCount()); | |
| 1590 | |
| 1591 // Restore one tab. | |
| 1592 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
| 1593 assertEquals("Unexpected number of tabs in first window.", 1, firstModel .getCount()); | |
| 1594 assertEquals("Unexpected number of tabs in second window.", 2, secondMod el.getCount()); | |
| 1595 | |
| 1596 // Restore one more tab. | |
| 1597 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
| 1598 | |
| 1599 // Check final states of both windows. | |
| 1600 Tab firstModelTab = firstModel.getTabAt(0); | |
|
Theresa
2016/07/12 17:25:17
nit: move this above line 1582, before any tabs ar
| |
| 1601 Tab secondModelTab = secondModel.getTabAt(0); | |
| 1602 Tab[] firstWindowTabs = new Tab[]{firstModelTab, firstModel.getTabAt(1)} ; | |
| 1603 Tab[] secondWindowTabs = new Tab[]{secondModelTab, secondModel.getTabAt( 1)}; | |
| 1604 checkState(firstModel, firstWindowTabs, firstModelTab, EMPTY, firstWindo wTabs, | |
| 1605 firstModelTab); | |
| 1606 checkState(secondModel, secondWindowTabs, secondModelTab, EMPTY, secondW indowTabs, | |
| 1607 secondModelTab); | |
| 1608 assertEquals(TEST_URL_0, firstWindowTabs[1].getUrl()); | |
| 1609 assertEquals(TEST_URL_1, secondWindowTabs[1].getUrl()); | |
| 1610 | |
| 1611 secondActivity.finishAndRemoveTask(); | |
| 1612 } | |
| 1613 | |
| 1614 /** | |
| 1615 * Test restoring closed tab from a closed window. | |
| 1616 * | Action | Result | |
| 1617 * 1. Create second window. | | |
| 1618 * 2. Open tab in window 2. | | |
| 1619 * 3. Close tab in window 2. | | |
| 1620 * 4. Close second window. | | |
| 1621 * 5. Restore tab. | Tab restored in first window. | |
| 1622 * @throws InterruptedException | |
| 1623 */ | |
| 1624 @MediumTest | |
| 1625 @MinAndroidSdkLevel(24) | |
| 1626 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 1627 public void testOpenRecentlyClosedTabMultiWindowFallback() throws Interrupte dException { | |
| 1628 final ChromeTabbedActivity2 secondActivity = | |
| 1629 MultiWindowUtilsTest.createSecondChromeTabbedActivity(getActivit y()); | |
| 1630 // Wait for the second window to be fully initialized. | |
| 1631 CriteriaHelper.pollUiThread(new Criteria() { | |
| 1632 @Override | |
| 1633 public boolean isSatisfied() { | |
| 1634 return secondActivity.getTabModelSelector().isTabStateInitialize d(); | |
| 1635 } | |
| 1636 }); | |
| 1637 | |
| 1638 // First window context. | |
| 1639 final TabModelSelector firstSelector = getActivity().getTabModelSelector (); | |
| 1640 final TabModel firstModel = firstSelector.getModel(false); | |
| 1641 | |
| 1642 // Second window context. | |
| 1643 final TabModel secondModel = secondActivity.getTabModelSelector().getMod el(false); | |
| 1644 | |
| 1645 // Create tab on second window. | |
| 1646 createFullyLoadedTabOnUiThread(secondActivity, TEST_URL_1); | |
| 1647 assertEquals("Window 2 should have 2 tab.", 2, secondModel.getCount()); | |
| 1648 | |
| 1649 // Close tab in second window, wait until tab restore service history is created. | |
| 1650 CallbackHelper closedCallback = new CallbackHelper(); | |
| 1651 secondModel.addObserver(new TabClosedObserver(closedCallback)); | |
| 1652 closeTabOnUiThread(secondModel, secondModel.getTabAt(1), false); | |
| 1653 | |
| 1654 try { | |
| 1655 closedCallback.waitForCallback(0); | |
| 1656 } catch (TimeoutException | InterruptedException e) { | |
| 1657 fail("Failed to close the tab on the second window."); | |
| 1658 } | |
| 1659 | |
| 1660 assertEquals("Window 2 should have 1 tab.", 1, secondModel.getCount()); | |
| 1661 | |
| 1662 // Closed the second window. Must wait until it's totally closed. | |
| 1663 int numExpectedActivities = ApplicationStatus.getRunningActivities().siz e() - 1; | |
| 1664 secondActivity.finishAndRemoveTask(); | |
| 1665 CriteriaHelper.pollUiThread(Criteria.equals(numExpectedActivities, new C allable<Integer>() { | |
| 1666 @Override | |
| 1667 public Integer call() { | |
| 1668 return ApplicationStatus.getRunningActivities().size(); | |
| 1669 } | |
| 1670 })); | |
| 1671 assertEquals("Window 1 should have 1 tab.", 1, firstModel.getCount()); | |
| 1672 | |
| 1673 // Restore closed tab from second window. It should be created in first window. | |
| 1674 openMostRecentlyClosedTabOnUiThread(firstSelector); | |
| 1675 assertEquals("Closed tab in second window should be restored in the firs t window.", 2, | |
| 1676 firstModel.getCount()); | |
| 1677 Tab[] firstWindowTabs = new Tab[]{firstModel.getTabAt(0), firstModel.get TabAt(1)}; | |
| 1678 checkState(firstModel, firstWindowTabs, firstWindowTabs[0], EMPTY, first WindowTabs, | |
| 1679 firstWindowTabs[0]); | |
| 1680 assertEquals(TEST_URL_1, firstWindowTabs[1].getUrl()); | |
| 1681 } | |
| 1413 } | 1682 } |
| OLD | NEW |