// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h"
#import "base/ios/ios_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/sys_string_conversions.h"
#import "components/prefs/pref_service.h"
#import "components/search_engines/template_url_service.h"
#import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider+Testing.h"
#import "ios/chrome/browser/context_menu/ui_bundled/context_menu_utils.h"
#import "ios/chrome/browser/favicon/model/favicon_loader.h"
#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_commands.h"
#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/photos/model/photos_availability.h"
#import "ios/chrome/browser/photos/model/photos_metrics.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/reading_list/model/reading_list_browser_agent.h"
#import "ios/chrome/browser/search_engines/model/search_engines_util.h"
#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
#import "ios/chrome/browser/shared/coordinator/alert/action_sheet_coordinator.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group_utils.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/public/commands/activity_service_commands.h"
#import "ios/chrome/browser/shared/public/commands/activity_service_share_url_command.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/lens_commands.h"
#import "ios/chrome/browser/shared/public/commands/mini_map_commands.h"
#import "ios/chrome/browser/shared/public/commands/reading_list_add_command.h"
#import "ios/chrome/browser/shared/public/commands/search_image_with_lens_command.h"
#import "ios/chrome/browser/shared/public/commands/unit_conversion_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/util/image/image_copier.h"
#import "ios/chrome/browser/shared/ui/util/image/image_saver.h"
#import "ios/chrome/browser/shared/ui/util/pasteboard_util.h"
#import "ios/chrome/browser/shared/ui/util/url_with_title.h"
#import "ios/chrome/browser/ui/lens/lens_availability.h"
#import "ios/chrome/browser/ui/lens/lens_entrypoint.h"
#import "ios/chrome/browser/ui/menu/browser_action_factory.h"
#import "ios/chrome/browser/ui/menu/menu_histograms.h"
#import "ios/chrome/browser/url_loading/model/image_search_param_generator.h"
#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
#import "ios/chrome/browser/web/model/image_fetch/image_fetch_tab_helper.h"
#import "ios/chrome/browser/web/model/web_navigation_util.h"
#import "ios/chrome/common/ui/favicon/favicon_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/public/provider/chrome/browser/context_menu/context_menu_api.h"
#import "ios/public/provider/chrome/browser/lens/lens_api.h"
#import "ios/web/common/features.h"
#import "ios/web/common/url_scheme_util.h"
#import "ios/web/public/ui/context_menu_params.h"
#import "ios/web/public/web_state.h"
#import "net/base/apple/url_conversions.h"
#import "ui/base/device_form_factor.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"
namespace {
// Maximum length for a context menu title formed from a URL.
const NSUInteger kContextMenuMaxURLTitleLength = 100;
// Character to append to context menut titles that are truncated.
NSString* const kContextMenuEllipsis = @"…";
// Maximum length for a context menu title formed from an address, date or phone
// number experience.
const NSUInteger kContextMenuMaxTitleLength = 30;
// Full URL alert accessibility identifier.
NSString* const kAlertAccessibilityIdentifier = @"AlertAccessibilityIdentifier";
} // namespace
@interface ContextMenuConfigurationProvider ()
// Helper for saving images.
@property(nonatomic, strong) ImageSaver* imageSaver;
// Helper for copying images.
@property(nonatomic, strong) ImageCopier* imageCopier;
@property(nonatomic, assign) Browser* browser;
@property(nonatomic, weak) UIViewController* baseViewController;
@property(nonatomic, assign, readonly) web::WebState* currentWebState;
@end
@implementation ContextMenuConfigurationProvider
- (instancetype)initWithBrowser:(Browser*)browser
baseViewController:(UIViewController*)baseViewController {
self = [super init];
if (self) {
_browser = browser;
_baseViewController = baseViewController;
_imageSaver = [[ImageSaver alloc] initWithBrowser:self.browser];
_imageCopier = [[ImageCopier alloc] initWithBrowser:self.browser];
}
return self;
}
- (void)stop {
_browser = nil;
_baseViewController = nil;
[_imageSaver stop];
_imageSaver = nil;
[_imageCopier stop];
_imageCopier = nil;
}
- (void)dealloc {
CHECK(!_browser);
}
- (UIContextMenuConfiguration*)
contextMenuConfigurationForWebState:(web::WebState*)webState
params:(web::ContextMenuParams)params {
UIContextMenuActionProvider actionProvider =
[self contextMenuActionProviderForWebState:webState params:params];
if (!actionProvider) {
return nil;
}
return
[UIContextMenuConfiguration configurationWithIdentifier:nil
previewProvider:nil
actionProvider:actionProvider];
}
#pragma mark - Properties
- (web::WebState*)currentWebState {
return self.browser ? self.browser->GetWebStateList()->GetActiveWebState()
: nullptr;
}
#pragma mark - Private
// Returns an action based contextual menu for a given web state (link, image,
// copy and intent detection actions).
- (UIContextMenuActionProvider)
contextMenuActionProviderForWebState:(web::WebState*)webState
params:(web::ContextMenuParams)params {
// Reset the URL.
_URLToLoad = GURL();
// Prevent context menu from displaying for a tab which is no longer the
// current one.
if (webState != self.currentWebState) {
return nil;
}
const GURL linkURL = params.link_url;
const bool isLink = linkURL.is_valid();
const GURL imageURL = params.src_url;
const bool isImage = imageURL.is_valid();
DCHECK(self.browser->GetBrowserState());
const bool isOffTheRecord = self.browser->GetBrowserState()->IsOffTheRecord();
const GURL& lastCommittedURL = webState->GetLastCommittedURL();
web::Referrer referrer(lastCommittedURL, web::ReferrerPolicyDefault);
NSMutableArray<UIMenuElement*>* menuElements = [[NSMutableArray alloc] init];
// TODO(crbug.com/40823789) add scenario for not a link and not an image.
MenuScenarioHistogram menuScenario =
isImage && isLink ? kMenuScenarioHistogramContextMenuImageLink
: isImage ? kMenuScenarioHistogramContextMenuImage
: kMenuScenarioHistogramContextMenuLink;
NSString* menuTitle = nil;
UIAction* showFullURL = nil;
if (isLink || isImage) {
menuTitle = GetContextMenuTitle(params);
if (!IsImageTitle(params) &&
menuTitle.length > kContextMenuMaxURLTitleLength + 1) {
if (base::FeatureList::IsEnabled(kShareInWebContextMenuIOS)) {
// "Show URL action" at the top of the context menu.
__weak __typeof(self) weakSelf = self;
NSString* URLString = base::SysUTF8ToNSString(params.link_url.spec());
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:menuScenario];
showFullURL = [actionFactory
actionToShowFullURL:menuTitle
block:^{
[weakSelf showFullURLPopUp:params
URLString:URLString];
}];
menuTitle = nil;
} else {
// Truncate context menu titles that originate from URLs, leaving text
// titles untruncated.
menuTitle = [[menuTitle substringToIndex:kContextMenuMaxURLTitleLength]
stringByAppendingString:kContextMenuEllipsis];
}
}
}
if (isLink) {
[menuElements
addObjectsFromArray:[self contextMenuElementsForLink:linkURL
scenario:menuScenario
referrer:referrer
isOffTheRecord:isOffTheRecord
params:params
showFullURLAction:showFullURL]];
}
if (isImage) {
[menuElements
addObjectsFromArray:[self imageContextMenuElementsWithURL:imageURL
scenario:menuScenario
referrer:referrer
isOffTheRecord:isOffTheRecord
isLink:isLink
webState:webState
params:params]];
}
// Insert any provided menu items. Do after Link and/or Image to allow
// inserting at beginning or adding to end.
ElementsToAddToContextMenu* result =
ios::provider::GetContextMenuElementsToAdd(
webState, params, self.baseViewController,
HandlerForProtocol(self.browser->GetCommandDispatcher(),
MiniMapCommands),
HandlerForProtocol(self.browser->GetCommandDispatcher(),
UnitConversionCommands));
if (result && result.elements) {
[menuElements addObjectsFromArray:result.elements];
menuTitle = result.title;
if (menuTitle.length > kContextMenuMaxTitleLength) {
menuTitle = [[menuTitle substringToIndex:kContextMenuMaxTitleLength - 1]
stringByAppendingString:kContextMenuEllipsis];
}
}
if (menuElements.count == 0) {
return nil;
}
UIMenu* menu = [UIMenu menuWithTitle:menuTitle children:menuElements];
UIContextMenuActionProvider actionProvider =
^(NSArray<UIMenuElement*>* suggestedActions) {
RecordMenuShown(menuScenario);
return menu;
};
return actionProvider;
}
// Returns the elements of the context menu related to links.
- (NSArray<UIMenuElement*>*)
contextMenuElementsForLink:(GURL)linkURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer
isOffTheRecord:(BOOL)isOffTheRecord
params:(web::ContextMenuParams)params
showFullURLAction:(UIAction*)showFullURLAction {
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
NSMutableArray<UIMenuElement*>* linkMenuElements =
[[NSMutableArray alloc] init];
__weak __typeof(self) weakSelf = self;
// Array for the actions/menus used to open a link.
NSMutableArray<UIMenuElement*>* linkOpeningElements =
[[NSMutableArray alloc] init];
if (showFullURLAction &&
base::FeatureList::IsEnabled(kShareInWebContextMenuIOS)) {
[linkOpeningElements addObject:showFullURLAction];
}
_URLToLoad = linkURL;
base::RecordAction(
base::UserMetricsAction("MobileWebContextMenuLinkImpression"));
if (web::UrlHasWebScheme(linkURL)) {
// Open in New Tab.
UrlLoadParams loadParams = UrlLoadParams::InNewTab(linkURL);
loadParams.SetInBackground(YES);
loadParams.web_params.referrer = referrer;
loadParams.in_incognito = isOffTheRecord;
loadParams.append_to = OpenPosition::kCurrentTab;
loadParams.origin_point = [params.view convertPoint:params.location
toView:nil];
UIAction* openNewTab = [actionFactory actionToOpenInNewTabWithBlock:^{
ContextMenuConfigurationProvider* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)->Load(loadParams);
}];
[linkOpeningElements addObject:openNewTab];
if (IsTabGroupInGridEnabled()) {
// Open in Tab Group.
UIMenuElement* openLinkInGroupMenu =
[self openLinkInGroupMenuElement:linkURL
scenario:scenario
referrer:referrer
isOffTheRecord:isOffTheRecord
params:params];
[linkOpeningElements addObject:openLinkInGroupMenu];
}
if (!isOffTheRecord) {
// Open in Incognito Tab.
UIAction* openIncognitoTab;
openIncognitoTab =
[actionFactory actionToOpenInNewIncognitoTabWithURL:linkURL
completion:nil];
[linkOpeningElements addObject:openIncognitoTab];
}
if (base::ios::IsMultipleScenesSupported()) {
// Open in New Window.
NSUserActivity* newWindowActivity = ActivityToLoadURL(
WindowActivityContextMenuOrigin, linkURL, referrer, isOffTheRecord);
UIAction* openNewWindow =
[actionFactory actionToOpenInNewWindowWithActivity:newWindowActivity];
[linkOpeningElements addObject:openNewWindow];
}
UIMenu* linkOpeningMenu = [UIMenu menuWithTitle:@""
image:nil
identifier:nil
options:UIMenuOptionsDisplayInline
children:linkOpeningElements];
[linkMenuElements addObject:linkOpeningMenu];
if (linkURL.SchemeIsHTTPOrHTTPS()) {
NSString* innerText = params.text;
if ([innerText length] > 0) {
// Add to reading list.
UIAction* addToReadingList =
[actionFactory actionToAddToReadingListWithBlock:^{
ContextMenuConfigurationProvider* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
ReadingListAddCommand* command =
[[ReadingListAddCommand alloc] initWithURL:linkURL
title:innerText];
ReadingListBrowserAgent* readingListBrowserAgent =
ReadingListBrowserAgent::FromBrowser(strongSelf.browser);
readingListBrowserAgent->AddURLsToReadingList(command.URLs);
}];
[linkMenuElements addObject:addToReadingList];
}
}
}
// Copy Link.
UIAction* copyLink =
[actionFactory actionToCopyURL:[[CrURL alloc] initWithGURL:linkURL]];
[linkMenuElements addObject:copyLink];
// Share Link.
if (base::FeatureList::IsEnabled(kShareInWebContextMenuIOS)) {
UIAction* shareLink = [actionFactory actionToShareWithBlock:^{
[weakSelf
shareURLFromContextMenu:linkURL
URLTitle:(params.text.length != 0) ? params.text
: params.alt_text
params:params];
}];
[linkMenuElements addObject:shareLink];
}
return linkMenuElements;
}
// Returns the elements of the context menu related to image links.
- (NSArray<UIMenuElement*>*)
imageContextMenuElementsWithURL:(GURL)imageURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer
isOffTheRecord:(BOOL)isOffTheRecord
isLink:(BOOL)isLink
webState:(web::WebState*)webState
params:(web::ContextMenuParams)params {
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
NSMutableArray<UIMenuElement*>* imageMenuElements =
[[NSMutableArray alloc] init];
base::RecordAction(
base::UserMetricsAction("MobileWebContextMenuImageImpression"));
__weak __typeof(self) weakSelf = self;
// Image saving.
NSArray<UIMenuElement*>* imageSavingElements =
[self imageSavingElementsWithURL:imageURL
scenario:scenario
referrer:referrer
webState:webState];
[imageMenuElements addObjectsFromArray:imageSavingElements];
// Copy Image.
UIAction* copyImage = [actionFactory actionCopyImageWithBlock:^{
ContextMenuConfigurationProvider* strongSelf = weakSelf;
if (!strongSelf || !strongSelf.baseViewController) {
return;
}
[strongSelf.imageCopier copyImageAtURL:imageURL
referrer:referrer
webState:strongSelf.currentWebState
baseViewController:strongSelf.baseViewController];
}];
[imageMenuElements addObject:copyImage];
// Image opening.
NSArray<UIMenuElement*>* imageOpeningElements =
[self imageOpeningElementsWithURL:imageURL
scenario:scenario
referrer:referrer
isOffTheRecord:isOffTheRecord
isLink:isLink
params:params];
[imageMenuElements addObjectsFromArray:imageOpeningElements];
// Image searching.
NSArray<UIMenuElement*>* imageSearchingElements =
[self imageSearchingElementsWithURL:imageURL
scenario:scenario
referrer:referrer];
[imageMenuElements addObjectsFromArray:imageSearchingElements];
return imageMenuElements;
}
// Searches an image with the given `imageURL` and `referrer`, optionally using
// Lens.
- (void)searchImageWithURL:(GURL)imageURL
usingLens:(BOOL)usingLens
referrer:(web::Referrer)referrer {
ImageFetchTabHelper* imageFetcher =
ImageFetchTabHelper::FromWebState(self.currentWebState);
DCHECK(imageFetcher);
__weak ContextMenuConfigurationProvider* weakSelf = self;
imageFetcher->GetImageData(imageURL, referrer, ^(NSData* data) {
if (usingLens) {
[weakSelf searchImageUsingLensWithData:data];
} else {
[weakSelf searchByImageData:data imageURL:imageURL];
}
});
}
// Starts a reverse image search based on `imageData` and `imageURL` in a new
// tab.
- (void)searchByImageData:(NSData*)imageData imageURL:(const GURL&)URL {
web::NavigationManager::WebLoadParams webParams =
ImageSearchParamGenerator::LoadParamsForImageData(
imageData, URL,
ios::TemplateURLServiceFactory::GetForBrowserState(
self.browser->GetBrowserState()));
const BOOL isIncognito = self.browser->GetBrowserState()->IsOffTheRecord();
// Apply variation header data to the params.
NSMutableDictionary<NSString*, NSString*>* combinedExtraHeaders =
[web_navigation_util::VariationHeadersForURL(webParams.url, isIncognito)
mutableCopy];
[combinedExtraHeaders addEntriesFromDictionary:webParams.extra_headers];
webParams.extra_headers = [combinedExtraHeaders copy];
UrlLoadParams params = UrlLoadParams::InNewTab(webParams);
params.in_incognito = isIncognito;
UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
}
// Searches an image with Lens using the given `imageData`.
- (void)searchImageUsingLensWithData:(NSData*)imageData {
id<LensCommands> handler =
HandlerForProtocol(_browser->GetCommandDispatcher(), LensCommands);
UIImage* image = [UIImage imageWithData:imageData];
SearchImageWithLensCommand* command = [[SearchImageWithLensCommand alloc]
initWithImage:image
entryPoint:LensEntrypoint::ContextMenu];
[handler searchImageWithLens:command];
}
// Returns the context menu element to open a URL (image or link) in a group.
- (UIMenuElement*)openLinkInGroupMenuElement:(GURL)linkURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer
isOffTheRecord:(BOOL)isOffTheRecord
params:(web::ContextMenuParams)params {
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
__weak __typeof(self) weakSelf = self;
std::set<const TabGroup*> groups =
GetAllGroupsForBrowserState(self.browser->GetBrowserState());
auto actionResult = ^(const TabGroup* group) {
ContextMenuConfigurationProvider* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
UrlLoadParams groupLoadParams = UrlLoadParams::InNewTab(linkURL);
groupLoadParams.SetInBackground(YES);
groupLoadParams.web_params.referrer = referrer;
groupLoadParams.in_incognito = isOffTheRecord;
groupLoadParams.append_to = OpenPosition::kCurrentTab;
groupLoadParams.origin_point = [params.view convertPoint:params.location
toView:nil];
groupLoadParams.load_in_group = true;
if (group) {
groupLoadParams.tab_group = group->GetWeakPtr();
}
UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)
->Load(groupLoadParams);
};
return [actionFactory menuToOpenLinkInGroupWithGroups:groups
block:actionResult];
}
// Returns the context menu elements for image opening.
- (NSArray<UIMenuElement*>*)
imageOpeningElementsWithURL:(GURL)imageURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer
isOffTheRecord:(BOOL)isOffTheRecord
isLink:(BOOL)isLink
params:(web::ContextMenuParams)params {
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
NSMutableArray<UIMenuElement*>* imageOpeningMenuElements =
[[NSMutableArray alloc] init];
// Open Image.
UIAction* openImage = [actionFactory actionOpenImageWithURL:imageURL
completion:nil];
[imageOpeningMenuElements addObject:openImage];
// Open Image in new tab.
UrlLoadParams loadParams = UrlLoadParams::InNewTab(imageURL);
loadParams.SetInBackground(YES);
loadParams.web_params.referrer = referrer;
loadParams.in_incognito = isOffTheRecord;
loadParams.append_to = OpenPosition::kCurrentTab;
loadParams.origin_point = [params.view convertPoint:params.location
toView:nil];
UIAction* openImageInNewTab =
[actionFactory actionOpenImageInNewTabWithUrlLoadParams:loadParams
completion:nil];
// Check if the URL was a valid link to avoid having the `Open in Tab Group`
// option twice.
if (!IsTabGroupInGridEnabled() || isLink) {
[imageOpeningMenuElements addObject:openImageInNewTab];
} else {
// Array for the actions/menus used to open an image in a new tab.
NSMutableArray<UIMenuElement*>* imageOpeningElements =
[[NSMutableArray alloc] init];
[imageOpeningElements addObject:openImageInNewTab];
// Open in Tab Group.
UIMenuElement* openLinkInGroupMenu =
[self openLinkInGroupMenuElement:imageURL
scenario:scenario
referrer:referrer
isOffTheRecord:isOffTheRecord
params:params];
[imageOpeningElements addObject:openLinkInGroupMenu];
UIMenu* imageOpeningMenu = [UIMenu menuWithTitle:@""
image:nil
identifier:nil
options:UIMenuOptionsDisplayInline
children:imageOpeningElements];
[imageOpeningMenuElements addObject:imageOpeningMenu];
}
return imageOpeningMenuElements;
}
// Returns the context menu elements for image saving.
- (NSArray<UIMenuElement*>*)
imageSavingElementsWithURL:(GURL)imageURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer
webState:(web::WebState*)webState {
const bool saveToPhotosAvailable =
IsSaveToPhotosAvailable(self.browser->GetBrowserState());
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
NSMutableArray<UIMenuElement*>* imageSavingElements =
[[NSMutableArray alloc] init];
__weak __typeof(self) weakSelf = self;
// Save Image.
UIAction* saveImage = [actionFactory actionSaveImageWithBlock:^{
ContextMenuConfigurationProvider* strongSelf = weakSelf;
if (!strongSelf || !strongSelf.baseViewController) {
return;
}
[strongSelf.imageSaver saveImageAtURL:imageURL
referrer:referrer
webState:strongSelf.currentWebState
baseViewController:strongSelf.baseViewController];
base::UmaHistogramEnumeration(
kSaveToPhotosContextMenuActionsHistogram,
saveToPhotosAvailable
? SaveToPhotosContextMenuActions::kAvailableDidSaveImageLocally
: SaveToPhotosContextMenuActions::kUnavailableDidSaveImageLocally);
}];
[imageSavingElements addObject:saveImage];
// Save Image to Photos.
if (saveToPhotosAvailable) {
base::RecordAction(base::UserMetricsAction(
"MobileWebContextMenuImageWithSaveToPhotosImpression"));
UIAction* saveImageToPhotosAction = [actionFactory
actionToSaveToPhotosWithImageURL:imageURL
referrer:referrer
webState:webState
block:^{
base::UmaHistogramEnumeration(
kSaveToPhotosContextMenuActionsHistogram,
SaveToPhotosContextMenuActions::
kAvailableDidSaveImageToGooglePhotos);
}];
[imageSavingElements addObject:saveImageToPhotosAction];
}
if (IsSaveToPhotosActionImprovementEnabled() && saveToPhotosAvailable) {
UIMenu* saveImageInMenu = [UIMenu
menuWithTitle:l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SAVE_IMAGE_IN)
image:DefaultSymbolWithPointSize(kPhotoBadgeArrowDownSymbol,
kSymbolActionPointSize)
identifier:nil
options:UIMenuOptionsSingleSelection
children:imageSavingElements];
return @[ saveImageInMenu ];
}
return imageSavingElements;
}
// Returns the context menu elements for image searching.
- (NSArray<UIMenuElement*>*)
imageSearchingElementsWithURL:(GURL)imageURL
scenario:(MenuScenarioHistogram)scenario
referrer:(web::Referrer)referrer {
BrowserActionFactory* actionFactory =
[[BrowserActionFactory alloc] initWithBrowser:self.browser
scenario:scenario];
__weak __typeof(self) weakSelf = self;
// Search the image using Lens if Lens is enabled and available. Otherwise
// fall back to a standard search by image experience.
TemplateURLService* service =
ios::TemplateURLServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
const BOOL useLens =
lens_availability::CheckAndLogAvailabilityForLensEntryPoint(
LensEntrypoint::ContextMenu,
search_engines::SupportsSearchImageWithLens(service));
NSMutableArray<UIMenuElement*>* imageSearchingMenuElements =
[[NSMutableArray alloc] init];
if (useLens) {
UIAction* searchImageWithLensAction =
[actionFactory actionToSearchImageUsingLensWithBlock:^{
[weakSelf searchImageWithURL:imageURL
usingLens:YES
referrer:referrer];
}];
[imageSearchingMenuElements addObject:searchImageWithLensAction];
}
if (!useLens && search_engines::SupportsSearchByImage(service)) {
const TemplateURL* defaultURL = service->GetDefaultSearchProvider();
NSString* title = l10n_util::GetNSStringF(
IDS_IOS_CONTEXT_MENU_SEARCHWEBFORIMAGE, defaultURL->short_name());
UIAction* searchByImage = [actionFactory
actionSearchImageWithTitle:title
Block:^{
[weakSelf searchImageWithURL:imageURL
usingLens:NO
referrer:referrer];
}];
[imageSearchingMenuElements addObject:searchByImage];
}
return imageSearchingMenuElements;
}
// Creates the UIAlertController with URLString that appears when clicking
// on Show Full URL button from the context menu.
- (void)showFullURLPopUp:(web::ContextMenuParams)params
URLString:(NSString*)URLString {
UIAlertController* alert =
[UIAlertController alertControllerWithTitle:@""
message:URLString
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction
actionWithTitle:l10n_util::GetNSString(IDS_IOS_CLOSE_ALERT_BUTTON_LABEL)
style:UIAlertActionStyleDefault
handler:nil];
[alert addAction:defaultAction];
alert.view.accessibilityIdentifier = kAlertAccessibilityIdentifier;
// The alert pop up will be presenting on top of the menu.
[self.baseViewController.presentedViewController presentViewController:alert
animated:YES
completion:nil];
}
// Calls the shareURLFromContextMenu with the given command.
- (void)shareURLFromContextMenu:(const GURL&)URLToShare
URLTitle:(NSString*)URLTitle
params:(web::ContextMenuParams)params {
id<ActivityServiceCommands> handler = HandlerForProtocol(
_browser->GetCommandDispatcher(), ActivityServiceCommands);
CGRect sourceRect = CGRectMake(params.location.x, params.location.y, 0, 0);
ActivityServiceShareURLCommand* command =
[[ActivityServiceShareURLCommand alloc] initWithURL:URLToShare
title:URLTitle
sourceView:params.view
sourceRect:sourceRect];
[handler shareURLFromContextMenu:command];
}
@end