| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // 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.contextmenu; |
| |
| import android.content.Context; |
| import android.net.MailTo; |
| import android.support.annotation.IntDef; |
| import android.support.annotation.StringRes; |
| import android.text.TextUtils; |
| import android.util.Pair; |
| import android.view.ContextMenu; |
| import android.webkit.MimeTypeMap; |
| |
| import org.chromium.base.CollectionUtil; |
| import org.chromium.base.CommandLine; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.ChromeFeatureList; |
| import org.chromium.chrome.browser.ChromeSwitches; |
| import org.chromium.chrome.browser.firstrun.FirstRunStatus; |
| import org.chromium.chrome.browser.preferences.ChromePreferenceManager; |
| import org.chromium.chrome.browser.preferences.datareduction.DataReductionProxyUma; |
| import org.chromium.chrome.browser.search_engines.TemplateUrlService; |
| import org.chromium.chrome.browser.util.UrlUtilities; |
| import org.chromium.content_public.common.ContentUrlConstants; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * A {@link ContextMenuPopulator} used for showing the default Chrome context menu. |
| */ |
| public class ChromeContextMenuPopulator implements ContextMenuPopulator { |
| private static final String TAG = "CCMenuPopulator"; |
| |
| /** |
| * Defines the Groups of each Context Menu Item |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({LINK, IMAGE, VIDEO}) |
| public @interface ContextMenuGroup {} |
| |
| public static final int LINK = 0; |
| public static final int IMAGE = 1; |
| public static final int VIDEO = 2; |
| |
| /** |
| * Defines the context menu modes |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({ |
| NORMAL_MODE, /* Default mode */ |
| CUSTOM_TAB_MODE, /* Custom tab mode */ |
| FULLSCREEN_TAB_MODE /* Full screen mode */ |
| }) |
| public @interface ContextMenuMode {} |
| |
| public static final int NORMAL_MODE = 0; |
| public static final int CUSTOM_TAB_MODE = 1; |
| public static final int FULLSCREEN_TAB_MODE = 2; |
| |
| // Items that are included in all context menus. |
| private static final Set<? extends ContextMenuItem> BASE_WHITELIST = |
| Collections.unmodifiableSet(CollectionUtil.newHashSet( |
| ChromeContextMenuItem.COPY_LINK_ADDRESS, ChromeContextMenuItem.CALL, |
| ChromeContextMenuItem.SEND_MESSAGE, ChromeContextMenuItem.ADD_TO_CONTACTS, |
| ChromeContextMenuItem.COPY, ChromeContextMenuItem.COPY_LINK_TEXT, |
| ChromeContextMenuItem.LOAD_ORIGINAL_IMAGE, ChromeContextMenuItem.SAVE_LINK_AS, |
| ChromeContextMenuItem.SAVE_IMAGE, ChromeContextMenuItem.SHARE_IMAGE, |
| ChromeContextMenuItem.SAVE_VIDEO)); |
| |
| // Items that are included for normal Chrome browser mode. |
| private static final Set<? extends ContextMenuItem> NORMAL_MODE_WHITELIST = |
| Collections.unmodifiableSet(CollectionUtil.newHashSet( |
| ChromeContextMenuItem.OPEN_IN_NEW_TAB, |
| ChromeContextMenuItem.OPEN_IN_OTHER_WINDOW, |
| ChromeContextMenuItem.OPEN_IN_INCOGNITO_TAB, ChromeContextMenuItem.SAVE_LINK_AS, |
| ChromeContextMenuItem.OPEN_IMAGE_IN_NEW_TAB, |
| ChromeContextMenuItem.SEARCH_BY_IMAGE)); |
| |
| // Additional items for custom tabs mode. |
| private static final Set<? extends ContextMenuItem> CUSTOM_TAB_MODE_WHITELIST = |
| Collections.unmodifiableSet(CollectionUtil.newHashSet(ChromeContextMenuItem.OPEN_IMAGE, |
| ChromeContextMenuItem.SEARCH_BY_IMAGE, |
| ChromeContextMenuItem.OPEN_IN_NEW_CHROME_TAB, |
| ChromeContextMenuItem.OPEN_IN_CHROME_INCOGNITO_TAB, |
| ChromeContextMenuItem.OPEN_IN_BROWSER_ID)); |
| |
| // Additional items for fullscreen tabs mode. |
| private static final Set<? extends ContextMenuItem> FULLSCREEN_TAB_MODE_WHITELIST = |
| Collections.unmodifiableSet( |
| CollectionUtil.newHashSet(ChromeContextMenuItem.OPEN_IN_CHROME)); |
| |
| // The order of the items within each lists determines the order of the context menu. |
| private static final List<? extends ContextMenuItem> CUSTOM_TAB_GROUP = |
| Collections.unmodifiableList( |
| CollectionUtil.newArrayList(ChromeContextMenuItem.OPEN_IN_NEW_CHROME_TAB, |
| ChromeContextMenuItem.OPEN_IN_CHROME_INCOGNITO_TAB, |
| ChromeContextMenuItem.OPEN_IN_BROWSER_ID)); |
| |
| private static final List<? extends ContextMenuItem> LINK_GROUP = Collections.unmodifiableList( |
| CollectionUtil.newArrayList(ChromeContextMenuItem.OPEN_IN_OTHER_WINDOW, |
| ChromeContextMenuItem.OPEN_IN_NEW_TAB, |
| ChromeContextMenuItem.OPEN_IN_INCOGNITO_TAB, |
| ChromeContextMenuItem.COPY_LINK_ADDRESS, ChromeContextMenuItem.COPY_LINK_TEXT, |
| ChromeContextMenuItem.SAVE_LINK_AS)); |
| |
| private static final List<? extends ContextMenuItem> IMAGE_GROUP = |
| Collections.unmodifiableList(CollectionUtil.newArrayList( |
| ChromeContextMenuItem.LOAD_ORIGINAL_IMAGE, ChromeContextMenuItem.SAVE_IMAGE, |
| ChromeContextMenuItem.OPEN_IMAGE, ChromeContextMenuItem.OPEN_IMAGE_IN_NEW_TAB, |
| ChromeContextMenuItem.SEARCH_BY_IMAGE, ChromeContextMenuItem.SHARE_IMAGE)); |
| |
| private static final List<? extends ContextMenuItem> MESSAGE_GROUP = |
| Collections.unmodifiableList(CollectionUtil.newArrayList(ChromeContextMenuItem.CALL, |
| ChromeContextMenuItem.SEND_MESSAGE, ChromeContextMenuItem.ADD_TO_CONTACTS, |
| ChromeContextMenuItem.COPY)); |
| |
| private static final List<? extends ContextMenuItem> VIDEO_GROUP = Collections.unmodifiableList( |
| CollectionUtil.newArrayList(ChromeContextMenuItem.SAVE_VIDEO)); |
| |
| private static final List<? extends ContextMenuItem> OTHER_GROUP = Collections.unmodifiableList( |
| CollectionUtil.newArrayList(ChromeContextMenuItem.OPEN_IN_CHROME)); |
| |
| private final ContextMenuItemDelegate mDelegate; |
| private final int mMode; |
| |
| static class ContextMenuUma { |
| // Note: these values must match the ContextMenuOption enum in histograms.xml. |
| static final int ACTION_OPEN_IN_NEW_TAB = 0; |
| static final int ACTION_OPEN_IN_INCOGNITO_TAB = 1; |
| static final int ACTION_COPY_LINK_ADDRESS = 2; |
| static final int ACTION_COPY_EMAIL_ADDRESS = 3; |
| static final int ACTION_COPY_LINK_TEXT = 4; |
| static final int ACTION_SAVE_LINK = 5; |
| static final int ACTION_SAVE_IMAGE = 6; |
| static final int ACTION_OPEN_IMAGE = 7; |
| static final int ACTION_OPEN_IMAGE_IN_NEW_TAB = 8; |
| static final int ACTION_SEARCH_BY_IMAGE = 11; |
| static final int ACTION_LOAD_ORIGINAL_IMAGE = 13; |
| static final int ACTION_SAVE_VIDEO = 14; |
| static final int ACTION_SHARE_IMAGE = 19; |
| static final int ACTION_OPEN_IN_OTHER_WINDOW = 20; |
| static final int ACTION_SEND_EMAIL = 23; |
| static final int ACTION_ADD_TO_CONTACTS = 24; |
| static final int ACTION_CALL = 30; |
| static final int ACTION_SEND_TEXT_MESSAGE = 31; |
| static final int ACTION_COPY_PHONE_NUMBER = 32; |
| static final int ACTION_OPEN_IN_NEW_CHROME_TAB = 33; |
| static final int ACTION_OPEN_IN_CHROME_INCOGNITO_TAB = 34; |
| static final int ACTION_OPEN_IN_BROWSER = 35; |
| static final int ACTION_OPEN_IN_CHROME = 36; |
| static final int NUM_ACTIONS = 37; |
| |
| // Note: these values must match the ContextMenuSaveLinkType enum in histograms.xml. |
| // Only add new values at the end, right before NUM_TYPES. We depend on these specific |
| // values in UMA histograms. |
| static final int TYPE_UNKNOWN = 0; |
| static final int TYPE_TEXT = 1; |
| static final int TYPE_IMAGE = 2; |
| static final int TYPE_AUDIO = 3; |
| static final int TYPE_VIDEO = 4; |
| static final int TYPE_PDF = 5; |
| static final int NUM_TYPES = 6; |
| |
| /** |
| * Records a histogram entry when the user selects an item from a context menu. |
| * @param params The ContextMenuParams describing the current context menu. |
| * @param action The action that the user selected (e.g. ACTION_SAVE_IMAGE). |
| */ |
| static void record(ContextMenuParams params, int action) { |
| assert action >= 0; |
| assert action < NUM_ACTIONS; |
| String histogramName; |
| if (params.isVideo()) { |
| histogramName = "ContextMenu.SelectedOption.Video"; |
| } else if (params.isImage()) { |
| histogramName = params.isAnchor() |
| ? "ContextMenu.SelectedOption.ImageLink" |
| : "ContextMenu.SelectedOption.Image"; |
| } else { |
| assert params.isAnchor(); |
| histogramName = "ContextMenu.SelectedOption.Link"; |
| } |
| RecordHistogram.recordEnumeratedHistogram(histogramName, action, NUM_ACTIONS); |
| } |
| |
| /** |
| * Records the content types when user downloads the file by long pressing the |
| * save link context menu option. |
| */ |
| static void recordSaveLinkTypes(String url) { |
| String extension = MimeTypeMap.getFileExtensionFromUrl(url); |
| int mimeType = TYPE_UNKNOWN; |
| if (extension != null) { |
| String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); |
| if (type != null) { |
| if (type.startsWith("text")) { |
| mimeType = TYPE_TEXT; |
| } else if (type.startsWith("image")) { |
| mimeType = TYPE_IMAGE; |
| } else if (type.startsWith("audio")) { |
| mimeType = TYPE_AUDIO; |
| } else if (type.startsWith("video")) { |
| mimeType = TYPE_VIDEO; |
| } else if (type.equals("application/pdf")) { |
| mimeType = TYPE_PDF; |
| } |
| } |
| } |
| RecordHistogram.recordEnumeratedHistogram( |
| "ContextMenu.SaveLinkType", mimeType, NUM_TYPES); |
| } |
| } |
| |
| /** |
| * Builds a {@link ChromeContextMenuPopulator}. |
| * @param delegate The {@link ContextMenuItemDelegate} that will be notified with actions |
| * to perform when menu items are selected. |
| * @param mode Defines the context menu mode |
| */ |
| public ChromeContextMenuPopulator(ContextMenuItemDelegate delegate, @ContextMenuMode int mode) { |
| mDelegate = delegate; |
| mMode = mode; |
| } |
| |
| @Override |
| public void onDestroy() { |
| mDelegate.onDestroy(); |
| } |
| |
| /** |
| * Gets the link of the item or the alternate text of an image. |
| * @return A string with either the link or with the alternate text. |
| */ |
| public static String createHeaderText(ContextMenuParams params) { |
| String titleText = ""; |
| if (!TextUtils.isEmpty(params.getLinkUrl()) |
| && !params.getLinkUrl().equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) { |
| titleText = params.getLinkUrl(); |
| } else if (!TextUtils.isEmpty(params.getTitleText())) { |
| titleText = params.getTitleText(); |
| } |
| return titleText; |
| } |
| |
| @Override |
| public List<Pair<Integer, List<ContextMenuItem>>> buildContextMenu( |
| ContextMenu menu, Context context, ContextMenuParams params) { |
| // Add all items in a group |
| Set<ContextMenuItem> supportedOptions = new HashSet<>(); |
| // Limit context menu to copying until FRE has been completed, |
| // unless FRE has been explicitly disabled (for full context menu in tests). |
| if (FirstRunStatus.getFirstRunFlowComplete() |
| || CommandLine.getInstance().hasSwitch( |
| ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)) { |
| supportedOptions.addAll(BASE_WHITELIST); |
| if (mMode == FULLSCREEN_TAB_MODE) { |
| supportedOptions.addAll(FULLSCREEN_TAB_MODE_WHITELIST); |
| } else if (mMode == CUSTOM_TAB_MODE) { |
| supportedOptions.addAll(CUSTOM_TAB_MODE_WHITELIST); |
| } else { |
| supportedOptions.addAll(NORMAL_MODE_WHITELIST); |
| } |
| } else { |
| supportedOptions.add(ChromeContextMenuItem.COPY_LINK_ADDRESS); |
| supportedOptions.add(ChromeContextMenuItem.COPY_LINK_TEXT); |
| supportedOptions.add(ChromeContextMenuItem.COPY); |
| } |
| |
| Set<ContextMenuItem> disabledOptions = getDisabledOptions(params); |
| // Split the items into their respective groups. |
| List<Pair<Integer, List<ContextMenuItem>>> groupedItems = new ArrayList<>(); |
| if (params.isAnchor() |
| && !ChromeFeatureList.isEnabled(ChromeFeatureList.CUSTOM_CONTEXT_MENU)) { |
| populateItemGroup(LINK, R.string.contextmenu_link_title, groupedItems, supportedOptions, |
| disabledOptions); |
| } |
| if (params.isImage()) { |
| populateItemGroup(IMAGE, R.string.contextmenu_image_title, groupedItems, |
| supportedOptions, disabledOptions); |
| } |
| if (params.isVideo()) { |
| populateItemGroup(VIDEO, R.string.contextmenu_video_title, groupedItems, |
| supportedOptions, disabledOptions); |
| } |
| if (params.isAnchor() |
| && ChromeFeatureList.isEnabled(ChromeFeatureList.CUSTOM_CONTEXT_MENU)) { |
| populateItemGroup(LINK, R.string.contextmenu_link_title, groupedItems, supportedOptions, |
| disabledOptions); |
| } |
| |
| // If there are no groups there still needs to be a way to add items from the OTHER_GROUP |
| // and CUSTOM_TAB_GROUP. |
| if (groupedItems.isEmpty()) { |
| int titleResId = R.string.contextmenu_link_title; |
| |
| if (params.isVideo()) { |
| titleResId = R.string.contextmenu_video_title; |
| } else if (params.isImage()) { |
| titleResId = R.string.contextmenu_image_title; |
| } |
| groupedItems.add(new Pair<Integer, List<ContextMenuItem>>( |
| titleResId, new ArrayList<ContextMenuItem>())); |
| } |
| |
| // These items don't belong to any official group so they are added to a possible visible |
| // list. |
| int index = ChromeFeatureList.isEnabled(ChromeFeatureList.CUSTOM_CONTEXT_MENU) |
| ? groupedItems.size() - 1 |
| : 0; |
| addValidItems(groupedItems.get(groupedItems.size() - 1 - index).second, OTHER_GROUP, |
| supportedOptions, disabledOptions); |
| if (mMode == CUSTOM_TAB_MODE) { |
| addValidItemsToFront(groupedItems.get(index).second, CUSTOM_TAB_GROUP, supportedOptions, |
| disabledOptions); |
| } |
| |
| // If there are no items from the extra items within OTHER_GROUP and CUSTOM_TAB_GROUP, then |
| // it's removed since there is nothing to show at all. |
| if (groupedItems.get(index).second.isEmpty()) { |
| groupedItems.remove(index); |
| } |
| |
| return groupedItems; |
| } |
| |
| private void populateItemGroup(@ContextMenuGroup int contextMenuType, @StringRes int titleResId, |
| List<Pair<Integer, List<ContextMenuItem>>> itemGroups, |
| Set<ContextMenuItem> supportedOptions, Set<ContextMenuItem> disabledOptions) { |
| List<ContextMenuItem> items = new ArrayList<>(); |
| switch (contextMenuType) { |
| case LINK: |
| addValidItems(items, LINK_GROUP, supportedOptions, disabledOptions); |
| addValidItems(items, MESSAGE_GROUP, supportedOptions, disabledOptions); |
| break; |
| case IMAGE: |
| addValidItems(items, IMAGE_GROUP, supportedOptions, disabledOptions); |
| break; |
| case VIDEO: |
| addValidItems(items, VIDEO_GROUP, supportedOptions, disabledOptions); |
| break; |
| default: |
| return; |
| } |
| |
| if (items.isEmpty()) return; |
| |
| itemGroups.add(new Pair<>(titleResId, items)); |
| } |
| |
| private static void addValidItems(List<ContextMenuItem> validItems, |
| List<? extends ContextMenuItem> allItems, Set<ContextMenuItem> supportedOptions, |
| Set<ContextMenuItem> disabledOptions) { |
| for (int i = 0; i < allItems.size(); i++) { |
| ContextMenuItem item = allItems.get(i); |
| if (supportedOptions.contains(item) && !disabledOptions.contains(item)) { |
| assert !validItems.contains(item); |
| validItems.add(item); |
| } |
| } |
| } |
| |
| /** |
| * This works in the same way as {@link #addValidItems(List, List, Set, Set)} however the list |
| * given for example (a, b, c) will be added to list d, e f, as a, b, c, d, e, f not |
| * c, b, a, d, e, f. |
| */ |
| private static void addValidItemsToFront(List<ContextMenuItem> validItems, |
| List<? extends ContextMenuItem> allItems, Set<ContextMenuItem> supportedOptions, |
| Set<ContextMenuItem> disabledOptions) { |
| for (int i = 0; i < allItems.size(); i++) { |
| ContextMenuItem item = allItems.get(i); |
| if (supportedOptions.contains(item) && !disabledOptions.contains(item)) { |
| assert !validItems.contains(item); |
| validItems.add(0, item); |
| } |
| } |
| } |
| |
| /** |
| * Given a set of params. It creates a list of items that should not be accessible in specific |
| * instances. |
| * @param params The parameters used to create a list of items that should not be allowed. |
| */ |
| private Set<ContextMenuItem> getDisabledOptions(ContextMenuParams params) { |
| Set<ContextMenuItem> disabledOptions = new HashSet<>(); |
| if (!params.isAnchor()) { |
| disabledOptions.addAll(LINK_GROUP); |
| } |
| if (!params.isImage()) { |
| disabledOptions.addAll(IMAGE_GROUP); |
| } |
| if (!params.isVideo()) { |
| disabledOptions.addAll(VIDEO_GROUP); |
| } |
| if (!MailTo.isMailTo(params.getLinkUrl()) |
| && !UrlUtilities.isTelScheme(params.getLinkUrl())) { |
| disabledOptions.addAll(MESSAGE_GROUP); |
| } |
| |
| if (params.isAnchor() && !mDelegate.isOpenInOtherWindowSupported()) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_OTHER_WINDOW); |
| } |
| |
| if (mDelegate.isIncognito() || !mDelegate.isIncognitoSupported()) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_INCOGNITO_TAB); |
| } |
| |
| if (params.getLinkText().trim().isEmpty() || params.isImage()) { |
| disabledOptions.add(ChromeContextMenuItem.COPY_LINK_TEXT); |
| } |
| |
| if (params.isAnchor() && !isAcceptedScheme(params.getLinkUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_OTHER_WINDOW); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_NEW_TAB); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_INCOGNITO_TAB); |
| } |
| |
| if (isEmptyUrl(params.getLinkUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_OTHER_WINDOW); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_NEW_TAB); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_INCOGNITO_TAB); |
| } |
| |
| if (MailTo.isMailTo(params.getLinkUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.COPY_LINK_TEXT); |
| disabledOptions.add(ChromeContextMenuItem.COPY_LINK_ADDRESS); |
| if (!mDelegate.supportsSendEmailMessage()) { |
| disabledOptions.add(ChromeContextMenuItem.SEND_MESSAGE); |
| } |
| if (TextUtils.isEmpty(MailTo.parse(params.getLinkUrl()).getTo()) |
| || !mDelegate.supportsAddToContacts()) { |
| disabledOptions.add(ChromeContextMenuItem.ADD_TO_CONTACTS); |
| } |
| disabledOptions.add(ChromeContextMenuItem.CALL); |
| } else if (UrlUtilities.isTelScheme(params.getLinkUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.COPY_LINK_TEXT); |
| disabledOptions.add(ChromeContextMenuItem.COPY_LINK_ADDRESS); |
| if (!mDelegate.supportsCall()) { |
| disabledOptions.add(ChromeContextMenuItem.CALL); |
| } |
| if (!mDelegate.supportsSendTextMessage()) { |
| disabledOptions.add(ChromeContextMenuItem.SEND_MESSAGE); |
| } |
| if (!mDelegate.supportsAddToContacts()) { |
| disabledOptions.add(ChromeContextMenuItem.ADD_TO_CONTACTS); |
| } |
| } |
| |
| if (!isDownloadableScheme(params.getLinkUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.SAVE_LINK_AS); |
| } |
| |
| boolean isSrcDownloadableScheme = isDownloadableScheme(params.getSrcUrl()); |
| if (params.isVideo()) { |
| boolean saveableAndDownloadable = params.canSaveMedia() && isSrcDownloadableScheme; |
| if (!saveableAndDownloadable) { |
| disabledOptions.add(ChromeContextMenuItem.SAVE_VIDEO); |
| } |
| } else if (params.isImage() && params.imageWasFetchedLoFi()) { |
| DataReductionProxyUma.previewsLoFiContextMenuAction( |
| DataReductionProxyUma.ACTION_LOFI_LOAD_IMAGE_CONTEXT_MENU_SHOWN); |
| // All image context menu items other than "Load image," "Open original image in |
| // new tab," and "Copy image URL" should be disabled on Lo-Fi images. |
| disabledOptions.add(ChromeContextMenuItem.SAVE_IMAGE); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IMAGE); |
| disabledOptions.add(ChromeContextMenuItem.SEARCH_BY_IMAGE); |
| disabledOptions.add(ChromeContextMenuItem.SHARE_IMAGE); |
| } else if (params.isImage() && !params.imageWasFetchedLoFi()) { |
| disabledOptions.add(ChromeContextMenuItem.LOAD_ORIGINAL_IMAGE); |
| |
| if (!isSrcDownloadableScheme) { |
| disabledOptions.add(ChromeContextMenuItem.SAVE_IMAGE); |
| } |
| |
| // Avoid showing open image option for same image which is already opened. |
| if (mDelegate.getPageUrl().equals(params.getSrcUrl())) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IMAGE); |
| } |
| final TemplateUrlService templateUrlServiceInstance = getTemplateUrlService(); |
| final boolean isSearchByImageAvailable = isSrcDownloadableScheme |
| && templateUrlServiceInstance.isLoaded() |
| && templateUrlServiceInstance.isSearchByImageAvailable() |
| && templateUrlServiceInstance.getDefaultSearchEngineTemplateUrl() != null; |
| |
| if (!isSearchByImageAvailable) { |
| disabledOptions.add(ChromeContextMenuItem.SEARCH_BY_IMAGE); |
| } |
| } |
| |
| if (mMode == CUSTOM_TAB_MODE) { |
| try { |
| URI uri = new URI(getUrl(params)); |
| if (UrlUtilities.isInternalScheme(uri) || isEmptyUrl(getUrl(params))) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_NEW_CHROME_TAB); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_CHROME_INCOGNITO_TAB); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_BROWSER_ID); |
| } else if (ChromePreferenceManager.getInstance().getCachedChromeDefaultBrowser()) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_BROWSER_ID); |
| if (!mDelegate.isIncognitoSupported()) { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_CHROME_INCOGNITO_TAB); |
| } |
| } else { |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_NEW_CHROME_TAB); |
| disabledOptions.add(ChromeContextMenuItem.OPEN_IN_CHROME_INCOGNITO_TAB); |
| } |
| } catch (URISyntaxException e) { |
| return disabledOptions; |
| } |
| } |
| |
| return disabledOptions; |
| } |
| |
| @Override |
| public boolean onItemSelected(ContextMenuHelper helper, ContextMenuParams params, int itemId) { |
| if (itemId == R.id.contextmenu_open_in_other_window) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_OTHER_WINDOW); |
| mDelegate.onOpenInOtherWindow(params.getLinkUrl(), params.getReferrer()); |
| } else if (itemId == R.id.contextmenu_open_in_new_tab) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_NEW_TAB); |
| mDelegate.onOpenInNewTab(params.getLinkUrl(), params.getReferrer()); |
| } else if (itemId == R.id.contextmenu_open_in_incognito_tab) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_INCOGNITO_TAB); |
| mDelegate.onOpenInNewIncognitoTab(params.getLinkUrl()); |
| } else if (itemId == R.id.contextmenu_open_image) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IMAGE); |
| mDelegate.onOpenImageUrl(params.getSrcUrl(), params.getReferrer()); |
| } else if (itemId == R.id.contextmenu_open_image_in_new_tab) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IMAGE_IN_NEW_TAB); |
| mDelegate.onOpenImageInNewTab(params.getSrcUrl(), params.getReferrer()); |
| } else if (itemId == R.id.contextmenu_load_original_image) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_LOAD_ORIGINAL_IMAGE); |
| DataReductionProxyUma.previewsLoFiContextMenuAction( |
| DataReductionProxyUma.ACTION_LOFI_LOAD_IMAGE_CONTEXT_MENU_CLICKED); |
| if (!mDelegate.wasLoadOriginalImageRequestedForPageLoad()) { |
| DataReductionProxyUma.previewsLoFiContextMenuAction( |
| DataReductionProxyUma.ACTION_LOFI_LOAD_IMAGE_CONTEXT_MENU_CLICKED_ON_PAGE); |
| } |
| mDelegate.onLoadOriginalImage(); |
| } else if (itemId == R.id.contextmenu_copy_link_address) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_COPY_LINK_ADDRESS); |
| mDelegate.onSaveToClipboard(params.getUnfilteredLinkUrl(), |
| ContextMenuItemDelegate.CLIPBOARD_TYPE_LINK_URL); |
| } else if (itemId == R.id.contextmenu_call) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_CALL); |
| mDelegate.onCall(params.getLinkUrl()); |
| } else if (itemId == R.id.contextmenu_send_message) { |
| if (MailTo.isMailTo(params.getLinkUrl())) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SEND_EMAIL); |
| mDelegate.onSendEmailMessage(params.getLinkUrl()); |
| } else if (UrlUtilities.isTelScheme(params.getLinkUrl())) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SEND_TEXT_MESSAGE); |
| mDelegate.onSendTextMessage(params.getLinkUrl()); |
| } |
| } else if (itemId == R.id.contextmenu_add_to_contacts) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_ADD_TO_CONTACTS); |
| mDelegate.onAddToContacts(params.getLinkUrl()); |
| } else if (itemId == R.id.contextmenu_copy) { |
| if (MailTo.isMailTo(params.getLinkUrl())) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_COPY_EMAIL_ADDRESS); |
| mDelegate.onSaveToClipboard(MailTo.parse(params.getLinkUrl()).getTo(), |
| ContextMenuItemDelegate.CLIPBOARD_TYPE_LINK_URL); |
| } else if (UrlUtilities.isTelScheme(params.getLinkUrl())) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_COPY_PHONE_NUMBER); |
| mDelegate.onSaveToClipboard(UrlUtilities.getTelNumber(params.getLinkUrl()), |
| ContextMenuItemDelegate.CLIPBOARD_TYPE_LINK_URL); |
| } |
| } else if (itemId == R.id.contextmenu_copy_link_text) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_COPY_LINK_TEXT); |
| mDelegate.onSaveToClipboard( |
| params.getLinkText(), ContextMenuItemDelegate.CLIPBOARD_TYPE_LINK_TEXT); |
| } else if (itemId == R.id.contextmenu_save_image) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SAVE_IMAGE); |
| if (mDelegate.startDownload(params.getSrcUrl(), false)) { |
| helper.startContextMenuDownload( |
| false, mDelegate.isDataReductionProxyEnabledForURL(params.getSrcUrl())); |
| } |
| } else if (itemId == R.id.contextmenu_save_video) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SAVE_VIDEO); |
| if (mDelegate.startDownload(params.getSrcUrl(), false)) { |
| helper.startContextMenuDownload(false, false); |
| } |
| } else if (itemId == R.id.contextmenu_save_link_as) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SAVE_LINK); |
| String url = params.getUnfilteredLinkUrl(); |
| if (mDelegate.startDownload(url, true)) { |
| ContextMenuUma.recordSaveLinkTypes(url); |
| helper.startContextMenuDownload(true, false); |
| } |
| } else if (itemId == R.id.contextmenu_search_by_image) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SEARCH_BY_IMAGE); |
| helper.searchForImage(); |
| } else if (itemId == R.id.contextmenu_share_image) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_SHARE_IMAGE); |
| helper.shareImage(); |
| } else if (itemId == R.id.menu_id_open_in_chrome) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_CHROME); |
| mDelegate.onOpenInChrome(params.getLinkUrl(), params.getPageUrl()); |
| } else if (itemId == R.id.contextmenu_open_in_new_chrome_tab) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_NEW_CHROME_TAB); |
| mDelegate.onOpenInNewChromeTabFromCCT(getUrl(params), false); |
| } else if (itemId == R.id.contextmenu_open_in_chrome_incognito_tab) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_CHROME_INCOGNITO_TAB); |
| mDelegate.onOpenInNewChromeTabFromCCT(getUrl(params), true); |
| } else if (itemId == R.id.contextmenu_open_in_browser_id) { |
| ContextMenuUma.record(params, ContextMenuUma.ACTION_OPEN_IN_BROWSER); |
| mDelegate.onOpenInDefaultBrowser(getUrl(params)); |
| } else { |
| assert false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @return Whether the scheme of the URL is valid . |
| */ |
| protected boolean isAcceptedScheme(String url) { |
| return UrlUtilities.isAcceptedScheme(url); |
| } |
| |
| /** |
| * @return Whether the scheme of the URL is valid for downloading. |
| */ |
| protected boolean isDownloadableScheme(String url) { |
| return UrlUtilities.isDownloadableScheme(url); |
| } |
| |
| /** |
| * @return The service that handles TemplateUrls. |
| */ |
| protected TemplateUrlService getTemplateUrlService() { |
| return TemplateUrlService.getInstance(); |
| } |
| |
| /** |
| * Checks whether a url is empty or blank. |
| * @param url The url need to be checked. |
| * @return True if the url is empty or "about:blank". |
| */ |
| private boolean isEmptyUrl(String url) { |
| if (TextUtils.isEmpty(url) || url.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Return the valid url of a ContextMenuParams. |
| * If the ContextMenuParams is an anchor and its linkUrl is not empty, returns the linkUrl. |
| * Otherwise returns the srcUrl. |
| * @param params The {@link ContextMenuParams} to check. |
| */ |
| private String getUrl(ContextMenuParams params) { |
| if (params.isAnchor()) { |
| return params.getLinkUrl(); |
| } else { |
| return params.getSrcUrl(); |
| } |
| } |
| } |