Commit 19f8abc5 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer - add Tab#dismissTransientUi

This API allows embedders to dismiss one active UI element, such as
a dialog, fullscreen or a selection popup. Its intended use is to
facilitate handling system back presses.

The implementation aligns WebLayer Shell with Chrome behavior in that
a back press will dismiss the active text selection popup rather than
navigating. It also simplifies the shell's onBackPressed somewhat,
and allows for future enhancements without involving the embedder.

Bug: 1056468
Change-Id: I1124515f2d8498847f19439a8e24ad84b1087ce7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2086323
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747502}
parent 3833be6d
......@@ -333,9 +333,26 @@ public class ModalDialogManager {
public void dismissDialogsOfType(
@ModalDialogType int dialogType, @DialogDismissalCause int dismissalCause) {
dismissPendingDialogsOfType(dialogType, dismissalCause);
dismissActiveDialogOfType(dialogType, dismissalCause);
}
/**
* Dismiss the dialog currently shown if it is of the specified type.
*
* Any pending dialogs will then be shown.
*
* @param dialogType The specified type of dialog.
* @param dismissalCause The {@link DialogDismissalCause} that describes why the dialogs are
* dismissed.
* @return true if a dialog was showing and was dismissed.
*/
public boolean dismissActiveDialogOfType(
@ModalDialogType int dialogType, @DialogDismissalCause int dismissalCause) {
if (isShowing() && dialogType == mCurrentType) {
dismissDialog(mCurrentPresenter.getDialogModel(), dismissalCause);
return true;
}
return false;
}
/** Helper method to dismiss pending dialogs of the specified type. */
......
......@@ -170,6 +170,42 @@ public class TabCallbackTest {
Assert.assertEquals(false, isTabModalShowingResult[0]);
}
@Test
@SmallTest
public void testDismissTransientUi() throws TimeoutException {
String pageUrl = mActivityTestRule.getTestDataURL("alert.html");
InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl);
Assert.assertNotNull(activity);
Boolean isTabModalShowingResult[] = new Boolean[1];
CallbackHelper callbackHelper = new CallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(() -> {
Tab tab = activity.getTab();
TabCallback callback = new TabCallback() {
@Override
public void onTabModalStateChanged(boolean isTabModalShowing) {
isTabModalShowingResult[0] = isTabModalShowing;
callbackHelper.notifyCalled();
}
};
tab.registerTabCallback(callback);
});
int callCount = callbackHelper.getCallCount();
EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView());
callbackHelper.waitForCallback(callCount);
Assert.assertEquals(true, isTabModalShowingResult[0]);
callCount = callbackHelper.getCallCount();
Assert.assertTrue(TestThreadUtils.runOnUiThreadBlockingNoException(
() -> activity.getTab().dismissTransientUi()));
callbackHelper.waitForCallback(callCount);
Assert.assertEquals(false, isTabModalShowingResult[0]);
Assert.assertFalse(TestThreadUtils.runOnUiThreadBlockingNoException(
() -> activity.getTab().dismissTransientUi()));
}
@Test
@SmallTest
public void testTabModalOverlayOnBackgroundTab() throws TimeoutException {
......
......@@ -211,8 +211,11 @@ public final class BrowserViewController
: mTopControlsContainerView.isTopControlVisible();
}
public void dismissTabModalOverlay() {
mModalDialogManager.dismissDialogsOfType(
ModalDialogType.TAB, DialogDismissalCause.TAB_SWITCHED);
/**
* @return true if a tab modal was showing and has been dismissed.
*/
public boolean dismissTabModalOverlay() {
return mModalDialogManager.dismissActiveDialogOfType(
ModalDialogType.TAB, DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE);
}
}
......@@ -414,6 +414,25 @@ public final class TabImpl extends ITab.Stub {
mWebContents.dispatchBeforeUnload(false);
}
@Override
public boolean dismissTransientUi() {
BrowserViewController viewController = getViewController();
if (viewController != null && viewController.dismissTabModalOverlay()) return true;
if (mWebContents.isFullscreenForCurrentTab()) {
mWebContents.exitFullscreen();
return true;
}
SelectionPopupController popup = SelectionPopupController.fromWebContents(mWebContents);
if (popup != null && popup.isSelectActionBarShowing()) {
popup.clearSelection();
return true;
}
return false;
}
@CalledByNative
private static RectF createRectF(float x, float y, float right, float bottom) {
return new RectF(x, y, right, bottom);
......
......@@ -38,4 +38,5 @@ interface ITab {
void dismissTabModalOverlay() = 10;
void dispatchBeforeUnloadAndClose() = 11;
boolean dismissTransientUi() = 12;
}
......@@ -195,9 +195,10 @@ public class Tab {
}
/**
* Dismisses any active tab modal overlays.
* Dismisses the active tab modal overlay.
*
* This has no effect if no tab modal dialog is currently displayed.
* If other tab modal dialogs are queued, they will then be shown. This has no effect if no tab
* modal dialog is currently displayed.
*
* See also {@link TabCallback#onTabModalStateChanged}.
*
......@@ -241,6 +242,30 @@ public class Tab {
}
}
/**
* Dismisses one active transient UI, if any.
*
* This is useful, for example, to handle presses on the system back button. UI such as tab
* modal dialogs, text selection popups and fullscreen will be dismissed. At most one piece of
* UI will be dismissed, but this distinction isn't very meaningful in practice since only one
* such kind of UI would tend to be active at a time.
*
* @return true if some piece of UI was dismissed, or false if nothing happened.
*
* @since 82
*/
public boolean dismissTransientUi() {
ThreadCheck.ensureOnUiThread();
if (WebLayer.getSupportedMajorVersionInternal() < 82) {
throw new UnsupportedOperationException();
}
try {
return mImpl.dismissTransientUi();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
public void setNewTabCallback(@Nullable NewTabCallback callback) {
ThreadCheck.ensureOnUiThread();
mNewTabCallback = callback;
......
......@@ -438,27 +438,18 @@ public class WebLayerShellActivity extends FragmentActivity {
@Override
public void onBackPressed() {
if (mExitFullscreenRunnable != null) {
mExitFullscreenRunnable.run();
return;
}
if (mBrowser != null) {
// The menu button is disabled only when there's a tab modal dialog. If this is the
// case, dismiss the overlay; if it works, consider the back press to have been handled.
if (!mMenuButton.isEnabled()) {
mBrowser.getActiveTab().dismissTabModalOverlay();
if (mMenuButton.isEnabled()) {
return;
}
}
Tab activeTab = mBrowser.getActiveTab();
if (activeTab.dismissTransientUi()) return;
NavigationController controller = mBrowser.getActiveTab().getNavigationController();
NavigationController controller = activeTab.getNavigationController();
if (controller.canGoBack()) {
controller.goBack();
return;
}
if (!mPreviousTabList.isEmpty()) {
mBrowser.getActiveTab().dispatchBeforeUnloadAndClose();
activeTab.dispatchBeforeUnloadAndClose();
return;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment