chromium/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenterTest.java

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.modaldialog;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertTrue;

import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.checkCurrentPresenter;
import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.checkDialogDismissalCause;
import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.checkPendingSize;
import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.createDialog;
import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.showDialog;
import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;

import android.view.View;
import android.view.ViewGroup;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features;
import org.chromium.base.test.util.RequiresRestart;
import org.chromium.base.test.util.Restriction;
import org.chromium.cc.input.BrowserControlsState;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.omnibox.UrlBar;
import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.chrome.test.util.OmniboxTestUtils;
import org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.test.util.UiRestriction;

/** Tests for {@link ChromeTabModalPresenter}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@Batch(Batch.PER_CLASS)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ChromeTabModalPresenterTest {
    private class TestObserver extends EmptyTabObserver
            implements UrlFocusChangeListener, ModalDialogTestUtils.TestDialogDismissedObserver {
        public final CallbackHelper onUrlFocusChangedCallback = new CallbackHelper();
        public final CallbackHelper onDialogDismissedCallback = new CallbackHelper();
        public final CallbackHelper onTabInteractabilityChangedCallback = new CallbackHelper();

        @Override
        public void onUrlFocusChange(boolean hasFocus) {
            onUrlFocusChangedCallback.notifyCalled();
        }

        @Override
        public void onInteractabilityChanged(Tab tab, boolean isInteractable) {
            onTabInteractabilityChangedCallback.notifyCalled();
        }

        @Override
        public void onDialogDismissed(int dismissalCause) {
            onDialogDismissedCallback.notifyCalled();
            checkDialogDismissalCause(mExpectedDismissalCause, dismissalCause);
        }
    }

    @ClassRule
    public static ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
            new BlankCTATabInitialStateRule(sActivityTestRule, true);

    private ChromeTabbedActivity mActivity;
    private ModalDialogManager mManager;
    private ChromeTabModalPresenter mTabModalPresenter;
    private TestObserver mTestObserver;
    private Integer mExpectedDismissalCause;
    private OmniboxTestUtils mOmnibox;

    @Before
    public void setUp() {
        mActivity = sActivityTestRule.getActivity();
        mOmnibox = new OmniboxTestUtils(mActivity);
        mManager = ThreadUtils.runOnUiThreadBlocking(mActivity::getModalDialogManager);
        mTestObserver = new TestObserver();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mActivity
                            .getToolbarManager()
                            .getToolbarLayoutForTesting()
                            .getLocationBar()
                            .getOmniboxStub()
                            .addUrlFocusChangeListener(mTestObserver);
                });
        mTabModalPresenter =
                (ChromeTabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
    }

    @After
    public void tearDown() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mManager.dismissAllDialogs(DialogDismissalCause.UNKNOWN);
                });
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testShow_UrlBarFocused() throws Exception {
        // Show a tab modal dialog. The dialog should be shown on top of the toolbar.
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        showDialog(mManager, dialog1, ModalDialogType.TAB);

        final View dialogContainer = mTabModalPresenter.getDialogContainerForTest();
        final View controlContainer = mActivity.findViewById(R.id.control_container);
        final ViewGroup containerParent = mTabModalPresenter.getContainerParentForTest();

        ensureDialogContainerVisible();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertThat(
                            containerParent.indexOfChild(dialogContainer),
                            Matchers.greaterThan(containerParent.indexOfChild(controlContainer)));
                });

        // When editing URL, it should be shown on top of the dialog.
        UrlBar urlBar = mActivity.findViewById(R.id.url_bar);
        int callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
        mOmnibox.requestFocus();
        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertThat(
                            containerParent.indexOfChild(dialogContainer),
                            Matchers.lessThan(containerParent.indexOfChild(controlContainer)));
                });

        // When URL bar is not focused, the dialog should be shown on top of the toolbar again.
        callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
        mOmnibox.clearFocus();
        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertThat(
                            containerParent.indexOfChild(dialogContainer),
                            Matchers.greaterThan(containerParent.indexOfChild(controlContainer)));
                });

        // Dismiss the dialog by clicking OK.
        onView(withText(R.string.ok)).perform(click());
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    @DisabledTest(message = "https://crbug.com/1420186")
    public void testSuspend_ToggleOverview() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> mActivity.getActivityTab().addObserver(mTestObserver));
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        PropertyModel dialog2 = createDialog(mActivity, mManager, "2", null);
        PropertyModel dialog3 = createDialog(mActivity, mManager, "3", null);

        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Add two tab modal dialogs available for showing.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        showDialog(mManager, dialog2, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        onViewWaiting(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        hasDescendant(withText("1")),
                                        not(hasDescendant(withText("2"))))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        //  Tab modal dialogs should be suspended on entering tab switcher.
        onView(withId(R.id.tab_switcher_button)).perform(click());
        checkPendingSize(mManager, ModalDialogType.TAB, 2);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        not(hasDescendant(withText("1"))),
                                        not(hasDescendant(withText("2"))))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // An app modal dialog can be shown in tab switcher.
        showDialog(mManager, dialog3, ModalDialogType.APP);
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 2);
        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
        onView(withText("3")).check(matches(isDisplayed()));
        checkCurrentPresenter(mManager, ModalDialogType.APP);

        // Close the app modal dialog and verify that the tab modal dialog is still queued.
        onView(withText(R.string.ok)).perform(click());
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 2);

        // Exit overview mode. The first dialog should be showing again.
        int callCount = mTestObserver.onTabInteractabilityChangedCallback.getCallCount();
        pressBack();
        mTestObserver.onTabInteractabilityChangedCallback.waitForCallback(callCount);

        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        ensureDialogContainerVisible();
        onView(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        hasDescendant(withText("1")),
                                        not(hasDescendant(withText("2"))))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Dismiss the first dialog. The second dialog should be shown.
        onView(withText(R.string.ok)).perform(click());
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        not(hasDescendant(withText("1"))),
                                        hasDescendant(withText("2")))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Reset states.
        ThreadUtils.runOnUiThreadBlocking(
                () -> mActivity.getActivityTab().removeObserver(mTestObserver));
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    public void testSuspend_LastTabClosed() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);

        // Make sure there is only one opened tab.
        while (mActivity.getCurrentTabModel().getCount() > 1) {
            ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
        }

        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Add a tab modal dialog available for showing.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ensureDialogContainerVisible();
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Tab modal dialogs should be suspended on entering tab switcher.
        onView(withId(R.id.tab_switcher_button)).perform(click());
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Close the only tab in the tab switcher. Verify that the queued tab modal dialogs are
        // cleared.
        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @DisabledTest(message = "https://crbug.com/1382221")
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    public void testSuspend_TabClosed() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        PropertyModel dialog2 = createDialog(mActivity, mManager, "2", null);
        PropertyModel dialog3 = createDialog(mActivity, mManager, "3", null);
        sActivityTestRule.loadUrlInNewTab("about:blank");

        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Add a tab modal dialog available for showing.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ensureDialogContainerVisible();
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Tab modal dialogs should be suspended on entering tab switcher.
        onView(withId(R.id.tab_switcher_button)).perform(click());
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Close current tab in the tab switcher.
        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Show a new tab modal dialog, and it should be suspended in tab switcher.
        showDialog(mManager, dialog2, ModalDialogType.TAB, false);
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Show an app modal dialog. The app modal dialog should be shown.
        showDialog(mManager, dialog3, ModalDialogType.APP);
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        onView(withText("3")).check(matches(isDisplayed()));
        checkCurrentPresenter(mManager, ModalDialogType.APP);
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testDismiss_SwitchTab() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        PropertyModel dialog2 = createDialog(mActivity, mManager, "2", null);

        // Open a new tab and make sure that the current tab is at index 0.
        sActivityTestRule.loadUrlInNewTab("about:blank");
        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);

        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Add a tab modal dialog available for showing.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ensureDialogContainerVisible();
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Dialog should be dismissed after switching to a different tab.
        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Open a tab modal dialog in the current tab. The dialog should be shown.
        showDialog(mManager, dialog2, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ensureDialogContainerVisible();
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("2"))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Features.DisableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
    public void testDismiss_BackPressed() {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        PropertyModel dialog2 = createDialog(mActivity, mManager, "2", null);

        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);

        // Add two tab modal dialogs available for showing. The first dialog should be shown first.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        showDialog(mManager, dialog2, ModalDialogType.TAB);
        checkPendingSize(mManager, ModalDialogType.TAB, 1);
        onViewWaiting(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Perform a back press. The first tab modal dialog should be dismissed.
        pressBack();
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        not(hasDescendant(withText("1"))),
                                        hasDescendant(withText("2")))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, true);
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Perform a second back press. The second tab modal dialog should be dismissed.
        pressBack();
        checkPendingSize(mManager, ModalDialogType.APP, 0);
        checkPendingSize(mManager, ModalDialogType.TAB, 0);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(
                        matches(
                                allOf(
                                        not(hasDescendant(withText("1"))),
                                        not(hasDescendant(withText("2"))))));
        ChromeModalDialogTestUtils.checkBrowserControls(mActivity, false);
        checkCurrentPresenter(mManager, null);
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Features.EnableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
    public void testDismiss_BackPressed_BackPressRefactor() {
        testDismiss_BackPressed();
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testDismiss_CancelOnTouchOutside() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);

        // Show a tab modal dialog and verify it shows.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        onViewWaiting(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Click dialog container and verify the dialog is not dismissed.
        final View dialogContainer = mTabModalPresenter.getDialogContainerForTest();
        assertTrue(dialogContainer.isClickable());
        ThreadUtils.runOnUiThreadBlocking(dialogContainer::performClick);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(hasDescendant(withText("1"))));
        checkCurrentPresenter(mManager, ModalDialogType.TAB);

        // Enable cancel on touch outside and verify the dialog is dismissed.
        ThreadUtils.runOnUiThreadBlocking(
                () -> dialog1.set(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true));
        assertTrue(dialogContainer.isClickable());
        ThreadUtils.runOnUiThreadBlocking(dialogContainer::performClick);
        onView(withId(R.id.tab_modal_dialog_container))
                .check(matches(not(hasDescendant(withText("1")))));
        checkCurrentPresenter(mManager, null);
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Features.DisableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
    public void testDismiss_DismissalCause_BackPressed() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", mTestObserver);
        mExpectedDismissalCause = DialogDismissalCause.NAVIGATE_BACK;

        showDialog(mManager, dialog1, ModalDialogType.TAB);

        // Dismiss the tab modal dialog and verify dismissal cause.
        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
        pressBack();
        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);

        mExpectedDismissalCause = null;
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    @Features.EnableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
    public void testDismiss_DismissalCause_BackPressed_BackPressRefactor() throws Exception {
        testDismiss_DismissalCause_BackPressed();
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testDismiss_DismissalCause_TabSwitched() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", mTestObserver);
        mExpectedDismissalCause = DialogDismissalCause.TAB_SWITCHED;
        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();

        // Open a new tab and make sure that the current tab is at index 0.
        sActivityTestRule.loadUrlInNewTab("about:blank");
        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);

        // Show a tab modal dialog and then switch tab.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);

        mExpectedDismissalCause = null;
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testDismiss_DismissalCause_TabDestroyed() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", mTestObserver);
        mExpectedDismissalCause = DialogDismissalCause.TAB_DESTROYED;
        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();

        // Show a tab modal dialog and then close tab.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);

        mExpectedDismissalCause = null;
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testDismiss_DismissalCause_TabNavigated() throws Exception {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", mTestObserver);
        mExpectedDismissalCause = DialogDismissalCause.NAVIGATE;
        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();

        // Show a tab modal dialog and then navigate to a different page.
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        EmbeddedTestServer server =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());
        sActivityTestRule.loadUrl(server.getURL("/chrome/test/data/android/simple.html"));
        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);

        mExpectedDismissalCause = null;
    }

    @Test
    @SmallTest
    @Feature({"ModalDialog"})
    public void testBrowserControlContraints_ShowHide() {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        Assert.assertEquals(BrowserControlsState.BOTH, getBrowserControlsConstraints());
        showDialog(mManager, dialog1, ModalDialogType.TAB);
        Assert.assertEquals(BrowserControlsState.SHOWN, getBrowserControlsConstraints());
        ThreadUtils.runOnUiThreadBlocking(
                () ->
                        mManager.dismissDialog(
                                dialog1, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED));
        Assert.assertEquals(BrowserControlsState.BOTH, getBrowserControlsConstraints());
    }

    @Test
    @SmallTest
    @RequiresRestart("Removing views is global and cannot be reversed.")
    @Feature({"ModalDialog"})
    // Ensures an exception isn't thrown when a dialog is dismissed and the View is no longer
    // attached to a Window. See https://crbug.com/1127254 for the specifics.
    public void testDismissAfterRemovingView() throws Throwable {
        PropertyModel dialog1 = createDialog(mActivity, mManager, "1", null);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mManager.showDialog(dialog1, ModalDialogType.TAB);
                    ViewGroup containerParent =
                            (ViewGroup) mTabModalPresenter.getContainerParentForTest();
                    // This is a bit hacky and intended to correspond to a case where the hosting
                    // ViewGroup is no longer attached to a Window.
                    containerParent.removeAllViews();
                    mManager.dismissAllDialogs(DialogDismissalCause.UNKNOWN);
                });
    }

    private @BrowserControlsState int getBrowserControlsConstraints() {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> mTabModalPresenter.getBrowserControlsVisibilityDelegate().get());
    }

    private void ensureDialogContainerVisible() {
        final View dialogContainer = mTabModalPresenter.getDialogContainerForTest();
        onViewWaiting(allOf(is(dialogContainer), isDisplayed()));
    }

    private void pressBack() {
        ThreadUtils.runOnUiThreadBlocking(mActivity.getOnBackPressedDispatcher()::onBackPressed);
    }
}