Commit d670fd60 authored by Javier Ernesto Flores Robles's avatar Javier Ernesto Flores Robles Committed by Commit Bot

[iOS][MF] Show an alert when a field won't be filled

Only fill credit cards on HTTPS, and passwords on HTTPS and password
fields. Show an alert to the user otherwise.

Bug: 905652, 878388, 845472
Change-Id: I51e0f07757dc4f7f1e002b88b83a2d2eeea071b5
Reviewed-on: https://chromium-review.googlesource.com/c/1340259
Commit-Queue: Javier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarStepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609980}
parent f2b4cc12
......@@ -879,6 +879,18 @@ locale. The strings in this file are specific to iOS.
<message name="IDS_IOS_MANUAL_FALLBACK_NO_USERNAME" desc="The title for disabled buttons on the manual fallback passwords UI when a credential doesn't contains a username. [30em]">
No Username
</message>
<message name="IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_OK_BUTTON" desc="The title of the OK button of the 'Not Secure' alert in manual fallback." meaning="The user will dismiss the alert [20em]">
OK
</message>
<message name="IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_TITLE" desc="The title of the alert informing a user the content they selected won't be filled on the form." meaning="The form is not secure [20em]">
Not secure
</message>
<message name="IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_PASSWORD_BODY" desc="The body of the alert informing a user the password they selected won't be filled on the form." meaning="The field the user is trying to fill is not of password type [80em]">
To protect your privacy, Chrome will not autofill your password in this field.
</message>
<message name="IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_GENERIC_BODY" desc="The body of the alert informing a user the content they selected won't be filled on the form." meaning="The form is not secure [80em]">
To protect your privacy, Chrome will not autofill this field.
</message>
<message name="IDS_IOS_MANUAL_FALLBACK_USE_OTHER_PASSWORD" desc="The title for the manual fallback UI with all passwords without filter." meaning="The title for a list where the user is about to select a password or username to be filled in a form [Length: 10em]">
Passwords
</message>
......
......@@ -30,6 +30,7 @@ source_set("autofill") {
"//components/prefs",
"//components/strings",
"//google_apis",
"//ios/chrome/app/strings",
"//ios/chrome/browser",
"//ios/chrome/browser/autofill",
"//ios/chrome/browser/autofill:autofill_shared",
......
......@@ -24,15 +24,18 @@
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
#include "ios/chrome/browser/ui/util/ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface FormInputAccessoryCoordinator ()<
ManualFillAccessoryViewControllerDelegate,
AutofillSecurityAlertPresenter,
AddressCoordinatorDelegate,
CardCoordinatorDelegate,
ManualFillAccessoryViewControllerDelegate,
PasswordCoordinatorDelegate,
PasswordFetcherDelegate,
PersonalDataManagerObserver> {
......@@ -84,7 +87,8 @@
_webStateList = webStateList;
_manualFillInjectionHandler =
[[ManualFillInjectionHandler alloc] initWithWebStateList:webStateList];
[[ManualFillInjectionHandler alloc] initWithWebStateList:webStateList
securityAlertPresenter:self];
_formInputAccessoryViewController =
[[FormInputAccessoryViewController alloc] init];
......@@ -279,4 +283,27 @@
personalDataManager->GetProfilesToSuggest().empty();
}
#pragma mark - AutofillSecurityAlertPresenter
- (void)presentSecurityWarningAlertWithText:(NSString*)body {
NSString* alertTitle =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_TITLE);
NSString* defaltActionTitle =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_OK_BUTTON);
UIAlertController* alert =
[UIAlertController alertControllerWithTitle:alertTitle
message:body
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction =
[UIAlertAction actionWithTitle:defaltActionTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action){
}];
[alert addAction:defaultAction];
[self.baseViewController presentViewController:alert
animated:YES
completion:nil];
}
@end
......@@ -128,10 +128,9 @@ NSString* const ManageCardsAccessibilityIdentifier =
[[ManualFillCreditCard alloc] initWithCreditCard:card];
// Don't replace the locked card with the unlocked one, so the user will
// have to unlock it again, if needed.
// TODO(crbug.com/845472): update userDidPickContent to have an isHttps
// parameter.
[self.contentDelegate userDidPickContent:manualFillCreditCard.number
isSecure:NO];
isPasswordField:NO
requiresHTTPS:YES];
}
- (void)onFullCardRequestFailed {
......
......@@ -449,7 +449,9 @@ static const CGFloat InnerMarginWidth = 16.0;
}
- (void)userDidTapAddressInfo:(UIButton*)sender {
[self.delegate userDidPickContent:sender.titleLabel.text isSecure:NO];
[self.delegate userDidPickContent:sender.titleLabel.text
isPasswordField:NO
requiresHTTPS:NO];
}
@end
......@@ -236,12 +236,16 @@ static const CGFloat ExpirationMarginWidth = 16.0;
if (!number.length) {
[self.navigationDelegate requestFullCreditCard:self.card];
} else {
[self.contentDelegate userDidPickContent:number isSecure:NO];
[self.contentDelegate userDidPickContent:number
isPasswordField:NO
requiresHTTPS:YES];
}
}
- (void)userDidTapCardInfo:(UIButton*)sender {
[self.contentDelegate userDidPickContent:sender.titleLabel.text isSecure:NO];
[self.contentDelegate userDidPickContent:sender.titleLabel.text
isPasswordField:NO
requiresHTTPS:NO];
}
@end
......@@ -15,9 +15,13 @@
// current form field.
//
// @param content The selected string.
// @param isSecure YES if the user selected a sensitive field, i.e. a password
// or a credit card.
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure;
// @param isPasswordField YES if the user selected content that requires a
// password field to be injected.
// @param requiresHTTPS YES if the user selected a field, that requires an HTTPS
// context to be injected.
- (void)userDidPickContent:(NSString*)content
isPasswordField:(BOOL)isPasswordField
requiresHTTPS:(BOOL)requiresHTTPS;
@end
......
......@@ -5,18 +5,30 @@
#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_INJECTION_HANDLER_H_
#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_INJECTION_HANDLER_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h"
@protocol AutofillSecurityAlertPresenter<NSObject>
// Presents an alert with the passed body. And a title indicating something is
// not secure. Credit card numbers and passwords cannot be filled over HTTP, and
// passwords can only be filled in a password field; when a user attempts to
// autofill these a warning is displayed using the security alert presenter
- (void)presentSecurityWarningAlertWithText:(NSString*)body;
@end
class WebStateList;
// Handler with the common logic for injecting data from manual fill.
@interface ManualFillInjectionHandler : NSObject<ManualFillContentDelegate>
// Returns a handler using the passed `WebStateList` in order to inject JS in
// the active web state.
- (instancetype)initWithWebStateList:(WebStateList*)webStateList;
// Returns a handler using the |WebStateList| to inject JS to the active web
// state and |securityAlertPresenter| to present alerts.
- (instancetype)initWithWebStateList:(WebStateList*)webStateList
securityAlertPresenter:
(id<AutofillSecurityAlertPresenter>)securityAlertPresenter;
@end
......
......@@ -20,11 +20,13 @@
#import "ios/chrome/browser/autofill/form_input_accessory_view_handler.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
#include "ios/web/public/web_state/web_frame.h"
#include "ios/web/public/web_state/web_frame_util.h"
#include "ios/web/public/web_state/web_frames_manager.h"
#include "ios/web/public/web_state/web_state.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
......@@ -51,8 +53,13 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
@property(nonatomic, assign) WebStateList* webStateList;
// YES if the last focused element is secure within its web frame. To be secure
// means it has a password type, the web is https and the URL can trusted.
@property(nonatomic, assign) BOOL lastFocusedElementIsSecure;
// means the web is HTTPS and the URL is trusted.
@property(nonatomic, assign, getter=isLastFocusedElementSecure)
BOOL lastFocusedElementSecure;
// YES if the last focused element is a password field.
@property(nonatomic, assign, getter=isLastFocusedElementPasswordField)
BOOL lastFocusedElementPasswordField;
// The last seen frame ID with focus activity.
@property(nonatomic, assign) std::string lastFocusedElementFrameIdentifier;
......@@ -60,24 +67,46 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
// The last seen focused element identifier.
@property(nonatomic, assign) std::string lastFocusedElementIdentifier;
// The view controller this object was initialized with.
@property(weak, nonatomic, nullable, readonly)
UIViewController* baseViewController;
// Used to present alerts.
@property(nonatomic, weak) id<AutofillSecurityAlertPresenter> alertPresenter;
@end
@implementation ManualFillInjectionHandler
- (instancetype)initWithWebStateList:(WebStateList*)webStateList {
- (instancetype)initWithWebStateList:(WebStateList*)webStateList
securityAlertPresenter:
(id<AutofillSecurityAlertPresenter>)securityAlertPresenter {
self = [super init];
if (self) {
_webStateList = webStateList;
_alertPresenter = securityAlertPresenter;
_formHelper =
[[FormObserverHelper alloc] initWithWebStateList:webStateList];
_formHelper.delegate = self;
}
return self;
}
#pragma mark - ManualFillViewDelegate
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure {
if (isSecure && !self.lastFocusedElementIsSecure) {
#pragma mark - ManualFillContentDelegate
- (void)userDidPickContent:(NSString*)content
isPasswordField:(BOOL)isPasswordField
requiresHTTPS:(BOOL)requiresHTTPS {
if (isPasswordField && ![self isLastFocusedElementPasswordField]) {
NSString* alertBody = l10n_util::GetNSString(
IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_PASSWORD_BODY);
[self.alertPresenter presentSecurityWarningAlertWithText:alertBody];
return;
}
if (requiresHTTPS && ![self isLastFocusedElementSecure]) {
NSString* alertBody =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_GENERIC_BODY);
[self.alertPresenter presentSecurityWarningAlertWithText:alertBody];
return;
}
[self fillLastSelectedFieldWithString:content];
......@@ -91,17 +120,16 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
if (params.type != "focus") {
return;
}
BOOL isContextSecure = autofill::IsContextSecureForWebState(webState);
BOOL isPasswordField = params.field_type == "password";
self.lastFocusedElementIsSecure = isContextSecure && isPasswordField;
self.lastFocusedElementSecure =
autofill::IsContextSecureForWebState(webState);
self.lastFocusedElementPasswordField = params.field_type == "password";
self.lastFocusedElementIdentifier = params.field_identifier;
if (autofill::switches::IsAutofillIFrameMessagingEnabled()) {
DCHECK(frame);
self.lastFocusedElementFrameIdentifier = frame->GetFrameId();
const GURL frameSecureOrigin = frame->GetSecurityOrigin();
if (!frameSecureOrigin.SchemeIsCryptographic()) {
self.lastFocusedElementIsSecure = NO;
self.lastFocusedElementSecure = NO;
}
}
}
......@@ -126,7 +154,7 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
return manager;
}
#pragma mark - Document Interaction
#pragma mark - Private
// Injects the passed string to the active field and jumps to the next field.
- (void)fillLastSelectedFieldWithString:(NSString*)string {
......
......@@ -185,14 +185,16 @@ static const CGFloat sideMargins = 16;
base::RecordAction(
base::UserMetricsAction("ManualFallback_Password_SelectUsername"));
[self.delegate userDidPickContent:self.manualFillCredential.username
isSecure:NO];
isPasswordField:NO
requiresHTTPS:NO];
}
- (void)userDidTapPasswordButton:(UIButton*)button {
base::RecordAction(
base::UserMetricsAction("ManualFallback_Password_SelectPassword"));
[self.delegate userDidPickContent:self.manualFillCredential.password
isSecure:YES];
isPasswordField:YES
requiresHTTPS:YES];
}
@end
......@@ -219,9 +219,13 @@ NSString* const OtherPasswordsAccessibilityIdentifier =
#pragma mark - ManualFillContentDelegate
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure {
- (void)userDidPickContent:(NSString*)content
isPasswordField:(BOOL)isPasswordField
requiresHTTPS:(BOOL)requiresHTTPS {
[self.navigationDelegate dismissPresentedViewController];
[self.contentDelegate userDidPickContent:content isSecure:isSecure];
[self.contentDelegate userDidPickContent:content
isPasswordField:isPasswordField
requiresHTTPS:requiresHTTPS];
}
@end
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