Commit f0976807 authored by Scott Violet's avatar Scott Violet Committed by Chromium LUCI CQ

cct: make it possible to hide 'open in chrome' menu items

Specifically from the context menu and app menu. There are separate
options for each one. This is only allowed if a service connection
is available as well as the right package.

BUG=1154709
TEST=covered by tests

Change-Id: If9b67cbb1ffbb629af3ae207b0aec85378e65ca8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2570238
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833412}
parent a411dd26
......@@ -526,4 +526,18 @@ public abstract class BrowserServicesIntentDataProvider {
public boolean shouldBlockNewNotificationRequests() {
return false;
}
/**
* Returns true if 'open in chrome' should be shown in the tab context menu.
*/
public boolean shouldShowOpenInChromeMenuItemInContextMenu() {
return true;
}
/**
* Returns true if 'open in chrome' should be shown in the app menu.
*/
public boolean shouldShowOpenInChromeMenuItem() {
return true;
}
}
......@@ -540,7 +540,8 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
.second;
if (mMode == ContextMenuMode.WEB_APP) {
items.add(createListItem(Item.OPEN_IN_CHROME));
} else if (mMode == ContextMenuMode.CUSTOM_TAB) {
} else if (mMode == ContextMenuMode.CUSTOM_TAB
&& mItemDelegate.supportsOpenInChromeFromCct()) {
boolean addNewEntries = false;
try {
URI uri = new URI(mParams.getUrl());
......
......@@ -327,7 +327,7 @@ public abstract class BaseCustomTabActivity extends ChromeActivity<BaseCustomTab
mIntentDataProvider.shouldShowShareMenuItem(),
mIntentDataProvider.shouldShowStarButton(),
mIntentDataProvider.shouldShowDownloadButton(), mIntentDataProvider.isIncognito(),
getModalDialogManager());
getModalDialogManager(), mIntentDataProvider.shouldShowOpenInChromeMenuItem());
}
@Override
......
......@@ -55,6 +55,7 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat
private final boolean mShowDownload;
private final boolean mIsOpenedByChrome;
private final boolean mIsIncognito;
private final boolean mShowOpenInChrome;
private final List<String> mMenuEntries;
private final Map<MenuItem, Integer> mItemToIndexMap = new HashMap<MenuItem, Integer>();
......@@ -63,6 +64,8 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat
/**
* Creates an {@link CustomTabAppMenuPropertiesDelegate} instance.
*
* @param showOpenInChrome Whether 'open in chrome' is shown, depending upon other state.
*/
public CustomTabAppMenuPropertiesDelegate(Context context,
ActivityTabProvider activityTabProvider,
......@@ -71,7 +74,7 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat
ObservableSupplier<BookmarkBridge> bookmarkBridgeSupplier, Verifier verifier,
@CustomTabsUiType final int uiType, List<String> menuEntries, boolean isOpenedByChrome,
boolean showShare, boolean showStar, boolean showDownload, boolean isIncognito,
ModalDialogManager modalDialogManager) {
ModalDialogManager modalDialogManager, boolean showOpenInChrome) {
super(context, activityTabProvider, multiWindowModeStateDispatcher, tabModelSelector,
toolbarManager, decorView, null, bookmarkBridgeSupplier, modalDialogManager);
mVerifier = verifier;
......@@ -82,6 +85,7 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat
mShowStar = showStar;
mShowDownload = showDownload;
mIsIncognito = isIncognito;
mShowOpenInChrome = showOpenInChrome;
}
@Override
......@@ -202,6 +206,9 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat
prepareTranslateMenuItem(menu, currentTab);
if (!mShowOpenInChrome) {
openInChromeItemVisible = false;
}
MenuItem openInChromeItem = menu.findItem(R.id.open_in_browser_id);
if (openInChromeItemVisible) {
String title = mIsIncognito ?
......
......@@ -356,6 +356,7 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
private final MultiWindowUtils mMultiWindowUtils;
private final PendingIntent mFocusIntent;
private final Verifier mVerifier;
private final boolean mShouldShowOpenInChromeMenuItemInContextMenu;
private TabWebContentsDelegateAndroid mWebContentsDelegateAndroid;
private ExternalNavigationDelegateImpl mNavigationDelegate;
......@@ -377,13 +378,15 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
* @param verifier Decides how to handle navigation to a new URL.
* @param ephemeralTabCoordinatorSupplier A provider of {@link EphemeralTabCoordinator} that
* shows preview tab.
* @param shouldShowOpenInChromeMenuItemInContextMenu Whether 'open in chrome' is shown.
*/
private CustomTabDelegateFactory(ChromeActivity<?> activity, boolean shouldHideBrowserControls,
boolean isOpenedByChrome, @Nullable String webApkScopeUrl,
@WebDisplayMode int displayMode, boolean shouldEnableEmbeddedMediaExperience,
BrowserControlsVisibilityDelegate visibilityDelegate, ExternalAuthUtils authUtils,
MultiWindowUtils multiWindowUtils, @Nullable PendingIntent focusIntent,
Verifier verifier, Lazy<EphemeralTabCoordinator> ephemeralTabCoordinator) {
Verifier verifier, Lazy<EphemeralTabCoordinator> ephemeralTabCoordinator,
boolean shouldShowOpenInChromeMenuItemInContextMenu) {
mActivity = activity;
mShouldHideBrowserControls = shouldHideBrowserControls;
mIsOpenedByChrome = isOpenedByChrome;
......@@ -397,6 +400,7 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
mFocusIntent = focusIntent;
mVerifier = verifier;
mEphemeralTabCoordinator = ephemeralTabCoordinator;
mShouldShowOpenInChromeMenuItemInContextMenu = shouldShowOpenInChromeMenuItemInContextMenu;
}
@Inject
......@@ -410,7 +414,8 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
getDisplayMode(intentDataProvider),
intentDataProvider.shouldEnableEmbeddedMediaExperience(), visibilityDelegate,
authUtils, multiWindowUtils, intentDataProvider.getFocusIntent(), verifier,
ephemeralTabCoordinator);
ephemeralTabCoordinator,
intentDataProvider.shouldShowOpenInChromeMenuItemInContextMenu());
}
/**
......@@ -419,7 +424,7 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
*/
static CustomTabDelegateFactory createDummy() {
return new CustomTabDelegateFactory(null, false, false, null, WebDisplayMode.BROWSER, false,
null, null, null, null, null, () -> null);
null, null, null, null, null, () -> null, true);
}
@Override
......@@ -465,19 +470,28 @@ public class CustomTabDelegateFactory implements TabDelegateFactory {
return new ExternalNavigationHandler(mNavigationDelegate);
}
@VisibleForTesting
TabContextMenuItemDelegate createTabContextMenuItemDelegate(Tab tab) {
TabModelSelector tabModelSelector =
mActivity != null ? mActivity.getTabModelSelector() : null;
return new TabContextMenuItemDelegate(tab, tabModelSelector,
EphemeralTabCoordinator.isSupported() ? mEphemeralTabCoordinator::get : ()
-> null,
() -> {}, mActivity == null ? null : mActivity::getSnackbarManager) {
@Override
public boolean supportsOpenInChromeFromCct() {
return mShouldShowOpenInChromeMenuItemInContextMenu;
}
};
}
@Override
public ContextMenuPopulatorFactory createContextMenuPopulatorFactory(Tab tab) {
@ChromeContextMenuPopulator.ContextMenuMode
int contextMenuMode = getContextMenuMode(mActivityType);
Supplier<ShareDelegate> shareDelegateSupplier =
mActivity == null ? null : mActivity.getShareDelegateSupplier();
TabModelSelector tabModelSelector =
mActivity != null ? mActivity.getTabModelSelector() : null;
return new ChromeContextMenuPopulatorFactory(
new TabContextMenuItemDelegate(tab, tabModelSelector,
EphemeralTabCoordinator.isSupported() ? mEphemeralTabCoordinator::get : ()
-> null,
() -> {}, mActivity == null ? null : mActivity::getSnackbarManager),
return new ChromeContextMenuPopulatorFactory(createTabContextMenuItemDelegate(tab),
shareDelegateSupplier, contextMenuMode, AppHooks.get().getExternalAuthUtils());
}
......
......@@ -171,6 +171,20 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid
public static final String EXTRA_HIDE_OMNIBOX_SUGGESTIONS_FROM_CCT =
"androidx.browser.customtabs.extra.HIDE_OMNIBOX_SUGGESTIONS_FROM_CCT";
/**
* Extra that determines whether the 'open in chrome' menu item should be shown in the context
* menu. The value is a boolean. Default value is false, meaning the item is shown.
*/
public static final String EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU =
"androidx.browser.customtabs.extra.HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU";
/**
* Extra that determines whether the 'open in chrome' menu item should be shown in the menu. The
* value is a boolean. Default value is false, meaning the item is shown.
*/
public static final String EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM =
"androidx.browser.customtabs.extra.HIDE_OPEN_IN_CHROME_MENU_ITEM";
/**
* Extra that, if set, results in marking visits from cct as hidden. The value is
* a boolean, and is only considered if the feature kCCTHideVisits is enabled.
......@@ -954,4 +968,24 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid
return IntentUtils.safeGetBooleanExtra(
mIntent, EXTRA_BLOCK_NEW_NOTIFICATION_REQUESTS_IN_CCT, false);
}
@Override
public boolean shouldShowOpenInChromeMenuItemInContextMenu() {
// Only 1p apps are allowed to hide visits.
String clientPackageName =
CustomTabsConnection.getInstance().getClientPackageNameForSession(getSession());
if (!GSAState.isGsaPackageName(clientPackageName)) return true;
return !IntentUtils.safeGetBooleanExtra(
mIntent, EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU, false);
}
@Override
public boolean shouldShowOpenInChromeMenuItem() {
// Only 1p apps are allowed to hide visits.
String clientPackageName =
CustomTabsConnection.getInstance().getClientPackageNameForSession(getSession());
if (!GSAState.isGsaPackageName(clientPackageName)) return true;
return !IntentUtils.safeGetBooleanExtra(
mIntent, EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM, false);
}
}
......@@ -317,6 +317,15 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
}
}
/**
* Returns whether the 'open in chrome' menu item should be shown. This is only called when the
* context menu is shown in cct.
*/
@Override
public boolean supportsOpenInChromeFromCct() {
return true;
}
@Override
public void onOpenInNewChromeTabFromCCT(String linkUrl, boolean isIncognito) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
......
......@@ -73,6 +73,8 @@ public class ChromeContextMenuPopulatorTest {
@Mock
private ChromeContextMenuPopulator mPopulator;
private boolean mSupportsOpenInChromeFromCct = true;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
......@@ -86,6 +88,8 @@ public class ChromeContextMenuPopulatorTest {
when(mItemDelegate.supportsSendEmailMessage()).thenReturn(true);
when(mItemDelegate.supportsSendTextMessage()).thenReturn(true);
when(mItemDelegate.supportsAddToContacts()).thenReturn(true);
when(mItemDelegate.supportsOpenInChromeFromCct())
.thenAnswer((mock) -> mSupportsOpenInChromeFromCct);
HashMap<String, Boolean> features = new HashMap<String, Boolean>();
features.put(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS, false);
......@@ -171,6 +175,23 @@ public class ChromeContextMenuPopulatorTest {
checkMenuOptions(expected4);
}
@Test
@SmallTest
@UiThreadTest
public void testShouldShowOpenInChromeMenuItemInContextMenu() {
FirstRunStatus.setFirstRunFlowComplete(true);
ContextMenuParams params = new ContextMenuParams(0, 0, PAGE_URL, LINK_URL, LINK_TEXT, "",
"", "", null, false, 0, 0, MenuSourceType.MENU_SOURCE_TOUCH);
// If the delegate returns false from supportsOpenInChromeFromCct() then open_in_chrome item
// should not be present.
mSupportsOpenInChromeFromCct = false;
initializePopulator(ChromeContextMenuPopulator.ContextMenuMode.CUSTOM_TAB, params);
int[] expected = {R.id.contextmenu_copy_link_address, R.id.contextmenu_copy_link_text,
R.id.contextmenu_save_link_as, R.id.contextmenu_share_link};
checkMenuOptions(expected);
}
@Test
@SmallTest
@UiThreadTest
......
......@@ -108,6 +108,7 @@ import org.chromium.chrome.browser.metrics.PageLoadMetrics;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabContextMenuItemDelegate;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabHidingType;
import org.chromium.chrome.browser.tab.TabLaunchType;
......@@ -2572,6 +2573,131 @@ public class CustomTabActivityTest {
.getActivityTab()
.getShouldBlockNewNotificationRequests());
}
@Test
@SmallTest
public void testHideOpenInChromeMenuItemInContextMenu() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
intent.setData(Uri.parse(mTestPage));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU,
true);
CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
CustomTabsConnection connection = CustomTabsConnection.getInstance();
connection.newSession(token);
connection.overridePackageNameForSessionForTesting(
token, "com.google.android.googlequicksearchbox");
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
TestThreadUtils.runOnUiThreadBlocking(() -> {
Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
Assert.assertTrue(tab != null);
Assert.assertTrue(
TabTestUtils.getDelegateFactory(tab) instanceof CustomTabDelegateFactory);
TabContextMenuItemDelegate tabContextMenuItemDelegate =
((CustomTabDelegateFactory) TabTestUtils.getDelegateFactory(tab))
.createTabContextMenuItemDelegate(tab);
// EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU should be propagated to the
// tabContextMenuItemDelegate.
Assert.assertFalse(tabContextMenuItemDelegate.supportsOpenInChromeFromCct());
});
}
@Test
@SmallTest
public void testHideOpenInChromeMenuItemInContextMenuIgnoredWrongPackage() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
intent.setData(Uri.parse(mTestPage));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU,
true);
CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
CustomTabsConnection connection = CustomTabsConnection.getInstance();
connection.newSession(token);
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
// EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU should be ignored without the right
// package.
TestThreadUtils.runOnUiThreadBlocking(() -> {
Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
Assert.assertTrue(tab != null);
Assert.assertTrue(
TabTestUtils.getDelegateFactory(tab) instanceof CustomTabDelegateFactory);
TabContextMenuItemDelegate tabContextMenuItemDelegate =
((CustomTabDelegateFactory) TabTestUtils.getDelegateFactory(tab))
.createTabContextMenuItemDelegate(tab);
// EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM_IN_CONTEXT_MENU should be ignored without the
// right package.
Assert.assertTrue(tabContextMenuItemDelegate.supportsOpenInChromeFromCct());
});
}
@Test
@SmallTest
public void testHideOpenInChromeMenuItem() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
intent.setData(Uri.parse(mTestPage));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CustomTabIntentDataProvider.EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM, true);
CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
CustomTabsConnection connection = CustomTabsConnection.getInstance();
connection.newSession(token);
connection.overridePackageNameForSessionForTesting(
token, "com.google.android.googlequicksearchbox");
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
final ActivityMonitor monitor =
InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, false);
openAppMenuAndAssertMenuShown();
PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
MenuItem item =
AppMenuTestSupport.getMenu(mCustomTabActivityTestRule.getAppMenuCoordinator())
.findItem(R.id.open_in_browser_id);
// The menu item should be there, but hidden.
Assert.assertNotNull(item);
Assert.assertFalse(item.isVisible());
});
}
@Test
@SmallTest
public void testHideOpenInChromeMenuItemVisibleWithWrongPackage() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
intent.setData(Uri.parse(mTestPage));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CustomTabIntentDataProvider.EXTRA_HIDE_OPEN_IN_CHROME_MENU_ITEM, true);
CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
CustomTabsConnection connection = CustomTabsConnection.getInstance();
connection.newSession(token);
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
final ActivityMonitor monitor =
InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, false);
openAppMenuAndAssertMenuShown();
PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
MenuItem item =
AppMenuTestSupport.getMenu(mCustomTabActivityTestRule.getAppMenuCoordinator())
.findItem(R.id.open_in_browser_id);
// As the package name doesn't match the expected package, the item should be visible.
Assert.assertNotNull(item);
Assert.assertTrue(item.isVisible());
});
}
/**
* The following test that history only has a single final page after speculation,
* whether it was a hit or a miss.
......
......@@ -185,6 +185,11 @@ public interface ContextMenuItemDelegate {
*/
void onOpenInChrome(String linkUrl, String pageUrl);
/**
* Returns true if menu entries should be added for open in chrome.
*/
boolean supportsOpenInChromeFromCct();
/**
* Called when the {@code url} should be opened in a new Chrome tab from CCT.
* @param linkUrl The URL to open.
......
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