0

Add Partial translate edit menu in WKWebView.

The entry should replace the "translate" entry and be displayed in the
same conditions.
If the translate entry cannot be found, place the entry before the
lookup menu.

If the edit menu new api is not enabled, add the entry at the end of
the menu using legacy API.

Bug: 1416451
Change-Id: Ibb3c6145b2ec03fa3b637c86cb8559bac87b8660
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4272005
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1107802}
This commit is contained in:
Olivier ROBIN
2023-02-21 17:26:48 +00:00
committed by Chromium LUCI CQ
parent bf3e7fb6e9
commit e75b414226
8 changed files with 151 additions and 8 deletions

@@ -1777,6 +1777,9 @@ While in Incognito, sites can't use cookies to see your browsing activity across
<message name="IDS_IOS_PARTIAL_TRANSLATE_ACTION_TRANSLATE_FULL_PAGE" desc="The action button to translate the full page (Title Case).">
Translate Full Page
</message>
<message name="IDS_IOS_PARTIAL_TRANSLATE_EDIT_MENU_ENTRY" desc="Action displayed in Edit Menu to trigger Partial Translate. As an action, it should be an infinitive verb, but should also respect the 'Google Translate' branding. Possible variations are 'Google Translate' or 'Translate with Google' are acceptable to meet those criteria [MAX_LENGTH=25em].">
Google Translate
</message>
<message name="IDS_IOS_PARTIAL_TRANSLATE_ERROR_GENERIC" desc="Error message when when the process failed (unknown reason).">
Partial translation failed.
</message>

@@ -0,0 +1 @@
71ce1af2b892c6c849ff7d0236616d7acd959458

@@ -63,6 +63,7 @@ source_set("ui") {
"//ios/chrome/app/strings",
"//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/link_to_text",
"//ios/chrome/browser/ui/partial_translate",
"//ios/chrome/browser/ui/util",
"//ios/chrome/common/ui/util",
"//ui/base",

@@ -106,6 +106,8 @@
id<BrowserCoordinatorCommands> handler =
HandlerForProtocol(dispatcher, BrowserCoordinatorCommands);
self.partialTranslateMediator.browserHandler = handler;
self.browserEditMenuHandler.partialTranslateDelegate =
self.partialTranslateMediator;
}
[self.webContentAreaOverlayContainerCoordinator start];

@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
@protocol LinkToTextDelegate;
@protocol PartialTranslateDelegate;
// A handler for the Browser edit menu.
// This class is in charge of customising the menu and executing the commands.
@@ -15,6 +16,10 @@
// The delegate to handle link to text button selection.
@property(nonatomic, weak) id<LinkToTextDelegate> linkToTextDelegate;
// The delegate to handle Partial Translate button selection.
@property(nonatomic, weak) id<PartialTranslateDelegate>
partialTranslateDelegate;
// Will be called by `BrowserContainerViewController buildMenuWithBuilder:`
// to customize its edit menu.
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder;

@@ -5,7 +5,9 @@
#import "ios/chrome/browser/ui/browser_container/browser_edit_menu_handler.h"
#import "base/feature_list.h"
#import "ios/chrome/browser/ui/link_to_text/link_to_text_mediator.h"
#import "base/mac/foundation_util.h"
#import "ios/chrome/browser/ui/link_to_text/link_to_text_delegate.h"
#import "ios/chrome/browser/ui/partial_translate/partial_translate_delegate.h"
#import "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/grit/ios_strings.h"
@@ -16,11 +18,24 @@
#endif
@interface BrowserEditMenuHandler ()
// A cache for the first responder that is reset on the next runloop.
@property(nonatomic, weak) UIResponder* firstResponder;
@end
@implementation BrowserEditMenuHandler
@implementation BrowserEditMenuHandler {
// Keep the original translate command to display partial translate in the
// same conditions.
UICommand* _originalTranslateCommand;
}
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder {
[self addLinkToText:builder];
[self addPartialTranslate:builder];
}
- (void)addLinkToText:(id<UIMenuBuilder>)builder {
if (!base::FeatureList::IsEnabled(kIOSCustomBrowserEditMenu)) {
return;
}
@@ -39,30 +54,132 @@
[builder insertChildMenu:linkToTextMenu atEndOfMenuForIdentifier:UIMenuRoot];
}
- (void)addPartialTranslate:(id<UIMenuBuilder>)builder {
if (!base::FeatureList::IsEnabled(kIOSEditMenuPartialTranslate)) {
return;
}
NSString* title =
l10n_util::GetNSString(IDS_IOS_PARTIAL_TRANSLATE_EDIT_MENU_ENTRY);
NSString* partialTranslateId = @"chromecommand.partialTranslate";
UICommand* partialTranslateCommand =
[UICommand commandWithTitle:title
image:nil
action:@selector(chromePartialTranslate:)
propertyList:partialTranslateId];
// Translate command is in the lookup menu.
// Retrieve the menu so it can be replaced with partial translate.
UIMenu* lookupMenu = [builder menuForIdentifier:UIMenuLookup];
NSArray* children = lookupMenu.children;
NSInteger translateIndex = -1;
for (NSUInteger index = 0; index < children.count; index++) {
UIMenuElement* element = children[index];
// Translate is a command.
if (![element isKindOfClass:[UICommand class]]) {
continue;
}
UICommand* command = base::mac::ObjCCast<UICommand>(element);
if (command.action != NSSelectorFromString(@"_translate:")) {
continue;
}
_originalTranslateCommand = command;
translateIndex = index;
break;
}
if (translateIndex == -1) {
// Translate command not found. Fallback adding the partial translate before
// the lookup menu.
// TODO(crbug.com/1417639): Catch this so it can be fixed.
UIMenu* partialTranslateMenu =
[UIMenu menuWithTitle:title
image:nil
identifier:partialTranslateId
options:UIMenuOptionsDisplayInline
children:@[ partialTranslateCommand ]];
[builder insertSiblingMenu:partialTranslateMenu
beforeMenuForIdentifier:UIMenuLookup];
return;
}
// Rebuild the lookup menu with partial translate
NSMutableArray* newChildren = [NSMutableArray arrayWithArray:children];
newChildren[translateIndex] = partialTranslateCommand;
UIMenu* newPartialTranslate = [UIMenu menuWithTitle:lookupMenu.title
image:lookupMenu.image
identifier:lookupMenu.identifier
options:lookupMenu.options
children:newChildren];
[builder replaceMenuForIdentifier:UIMenuLookup withMenu:newPartialTranslate];
}
- (void)addEditMenuEntries {
if (base::FeatureList::IsEnabled(kIOSCustomBrowserEditMenu)) {
return;
}
if (!base::FeatureList::IsEnabled(kSharedHighlightingIOS)) {
return;
if (base::FeatureList::IsEnabled(kSharedHighlightingIOS)) {
NSString* title = l10n_util::GetNSString(IDS_IOS_SHARE_LINK_TO_TEXT);
UIMenuItem* menuItem =
[[UIMenuItem alloc] initWithTitle:title action:@selector(linkToText:)];
RegisterEditMenuItem(menuItem);
}
if (base::FeatureList::IsEnabled(kIOSEditMenuPartialTranslate)) {
NSString* title =
l10n_util::GetNSString(IDS_IOS_PARTIAL_TRANSLATE_EDIT_MENU_ENTRY);
UIMenuItem* menuItem =
[[UIMenuItem alloc] initWithTitle:title
action:@selector(chromePartialTranslate:)];
RegisterEditMenuItem(menuItem);
}
NSString* title = l10n_util::GetNSString(IDS_IOS_SHARE_LINK_TO_TEXT);
UIMenuItem* menuItem =
[[UIMenuItem alloc] initWithTitle:title action:@selector(linkToText:)];
RegisterEditMenuItem(menuItem);
}
- (BOOL)canPerformChromeAction:(SEL)action withSender:(id)sender {
if (action == @selector(linkToText:)) {
return [self.linkToTextDelegate shouldOfferLinkToText];
}
if (action == @selector(chromePartialTranslate:)) {
BOOL canHandlePartialTranslate =
[self.partialTranslateDelegate canHandlePartialTranslateSelection];
if (canHandlePartialTranslate && [self firstResponder] &&
_originalTranslateCommand) {
return [[self firstResponder]
canPerformAction:_originalTranslateCommand.action
withSender:_originalTranslateCommand];
}
return canHandlePartialTranslate;
}
return NO;
}
#pragma mark - LinkToTextDelegate methods
- (void)linkToText:(UIMenuItem*)item {
DCHECK(base::FeatureList::IsEnabled(kSharedHighlightingIOS));
DCHECK(self.linkToTextDelegate);
[self.linkToTextDelegate handleLinkToTextSelection];
}
#pragma mark - PartialTranslateDelegate methods
- (void)chromePartialTranslate:(UIMenuItem*)item {
DCHECK(base::FeatureList::IsEnabled(kIOSEditMenuPartialTranslate));
DCHECK(self.partialTranslateDelegate);
[self.partialTranslateDelegate handlePartialTranslateSelection];
}
#pragma mark - private methods
- (UIResponder*)firstResponder {
if (_firstResponder) {
return _firstResponder;
}
_firstResponder = GetFirstResponder();
__weak BrowserEditMenuHandler* weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.firstResponder = nil;
});
return _firstResponder;
}
@end

@@ -11,6 +11,9 @@
// Handles the link to text menu item selection.
- (void)handlePartialTranslateSelection;
// Returns whether a partial translate can be handled.
- (BOOL)canHandlePartialTranslateSelection;
@end
#endif // IOS_CHROME_BROWSER_UI_PARTIAL_TRANSLATE_PARTIAL_TRANSLATE_DELEGATE_H_

@@ -71,6 +71,17 @@
}));
}
- (BOOL)canHandlePartialTranslateSelection {
DCHECK(base::FeatureList::IsEnabled(kSharedHighlightingIOS));
// TODO(crbug.com/1417238): add metrics
WebSelectionTabHelper* tabHelper = [self webSelectionTabHelper];
if (!tabHelper) {
return NO;
}
return tabHelper->CanRetrieveSelectedText() &&
PartialTranslateLimitMaxCharacters() > 0u;
}
- (void)switchToFullTranslateWithMessage:(NSString*)message {
// TODO(crbug.com/1417238): add metrics
if (!self.alertDelegate) {