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. ...@@ -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]"> <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 No Username
</message> </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]"> <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 Passwords
</message> </message>
......
...@@ -30,6 +30,7 @@ source_set("autofill") { ...@@ -30,6 +30,7 @@ source_set("autofill") {
"//components/prefs", "//components/prefs",
"//components/strings", "//components/strings",
"//google_apis", "//google_apis",
"//ios/chrome/app/strings",
"//ios/chrome/browser", "//ios/chrome/browser",
"//ios/chrome/browser/autofill", "//ios/chrome/browser/autofill",
"//ios/chrome/browser/autofill:autofill_shared", "//ios/chrome/browser/autofill:autofill_shared",
......
...@@ -24,15 +24,18 @@ ...@@ -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/manual_fill_injection_handler.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h" #import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
#include "ios/chrome/browser/ui/util/ui_util.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) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface FormInputAccessoryCoordinator ()< @interface FormInputAccessoryCoordinator ()<
ManualFillAccessoryViewControllerDelegate, AutofillSecurityAlertPresenter,
AddressCoordinatorDelegate, AddressCoordinatorDelegate,
CardCoordinatorDelegate, CardCoordinatorDelegate,
ManualFillAccessoryViewControllerDelegate,
PasswordCoordinatorDelegate, PasswordCoordinatorDelegate,
PasswordFetcherDelegate, PasswordFetcherDelegate,
PersonalDataManagerObserver> { PersonalDataManagerObserver> {
...@@ -84,7 +87,8 @@ ...@@ -84,7 +87,8 @@
_webStateList = webStateList; _webStateList = webStateList;
_manualFillInjectionHandler = _manualFillInjectionHandler =
[[ManualFillInjectionHandler alloc] initWithWebStateList:webStateList]; [[ManualFillInjectionHandler alloc] initWithWebStateList:webStateList
securityAlertPresenter:self];
_formInputAccessoryViewController = _formInputAccessoryViewController =
[[FormInputAccessoryViewController alloc] init]; [[FormInputAccessoryViewController alloc] init];
...@@ -279,4 +283,27 @@ ...@@ -279,4 +283,27 @@
personalDataManager->GetProfilesToSuggest().empty(); 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 @end
...@@ -128,10 +128,9 @@ NSString* const ManageCardsAccessibilityIdentifier = ...@@ -128,10 +128,9 @@ NSString* const ManageCardsAccessibilityIdentifier =
[[ManualFillCreditCard alloc] initWithCreditCard:card]; [[ManualFillCreditCard alloc] initWithCreditCard:card];
// Don't replace the locked card with the unlocked one, so the user will // Don't replace the locked card with the unlocked one, so the user will
// have to unlock it again, if needed. // have to unlock it again, if needed.
// TODO(crbug.com/845472): update userDidPickContent to have an isHttps
// parameter.
[self.contentDelegate userDidPickContent:manualFillCreditCard.number [self.contentDelegate userDidPickContent:manualFillCreditCard.number
isSecure:NO]; isPasswordField:NO
requiresHTTPS:YES];
} }
- (void)onFullCardRequestFailed { - (void)onFullCardRequestFailed {
......
...@@ -449,7 +449,9 @@ static const CGFloat InnerMarginWidth = 16.0; ...@@ -449,7 +449,9 @@ static const CGFloat InnerMarginWidth = 16.0;
} }
- (void)userDidTapAddressInfo:(UIButton*)sender { - (void)userDidTapAddressInfo:(UIButton*)sender {
[self.delegate userDidPickContent:sender.titleLabel.text isSecure:NO]; [self.delegate userDidPickContent:sender.titleLabel.text
isPasswordField:NO
requiresHTTPS:NO];
} }
@end @end
...@@ -236,12 +236,16 @@ static const CGFloat ExpirationMarginWidth = 16.0; ...@@ -236,12 +236,16 @@ static const CGFloat ExpirationMarginWidth = 16.0;
if (!number.length) { if (!number.length) {
[self.navigationDelegate requestFullCreditCard:self.card]; [self.navigationDelegate requestFullCreditCard:self.card];
} else { } else {
[self.contentDelegate userDidPickContent:number isSecure:NO]; [self.contentDelegate userDidPickContent:number
isPasswordField:NO
requiresHTTPS:YES];
} }
} }
- (void)userDidTapCardInfo:(UIButton*)sender { - (void)userDidTapCardInfo:(UIButton*)sender {
[self.contentDelegate userDidPickContent:sender.titleLabel.text isSecure:NO]; [self.contentDelegate userDidPickContent:sender.titleLabel.text
isPasswordField:NO
requiresHTTPS:NO];
} }
@end @end
...@@ -15,9 +15,13 @@ ...@@ -15,9 +15,13 @@
// current form field. // current form field.
// //
// @param content The selected string. // @param content The selected string.
// @param isSecure YES if the user selected a sensitive field, i.e. a password // @param isPasswordField YES if the user selected content that requires a
// or a credit card. // password field to be injected.
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure; // @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 @end
......
...@@ -5,18 +5,30 @@ ...@@ -5,18 +5,30 @@
#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_INJECTION_HANDLER_H_ #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_ #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" #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; class WebStateList;
// Handler with the common logic for injecting data from manual fill. // Handler with the common logic for injecting data from manual fill.
@interface ManualFillInjectionHandler : NSObject<ManualFillContentDelegate> @interface ManualFillInjectionHandler : NSObject<ManualFillContentDelegate>
// Returns a handler using the passed `WebStateList` in order to inject JS in // Returns a handler using the |WebStateList| to inject JS to the active web
// the active web state. // state and |securityAlertPresenter| to present alerts.
- (instancetype)initWithWebStateList:(WebStateList*)webStateList; - (instancetype)initWithWebStateList:(WebStateList*)webStateList
securityAlertPresenter:
(id<AutofillSecurityAlertPresenter>)securityAlertPresenter;
@end @end
......
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#import "ios/chrome/browser/autofill/form_input_accessory_view_handler.h" #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/ui/autofill/manual_fill/form_observer_helper.h"
#import "ios/chrome/browser/web_state_list/web_state_list.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" #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.h"
#include "ios/web/public/web_state/web_frame_util.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_frames_manager.h"
#include "ios/web/public/web_state/web_state.h" #include "ios/web/public/web_state/web_state.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "url/gurl.h" #include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -51,8 +53,13 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1; ...@@ -51,8 +53,13 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
@property(nonatomic, assign) WebStateList* webStateList; @property(nonatomic, assign) WebStateList* webStateList;
// YES if the last focused element is secure within its web frame. To be secure // 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. // means the web is HTTPS and the URL is trusted.
@property(nonatomic, assign) BOOL lastFocusedElementIsSecure; @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. // The last seen frame ID with focus activity.
@property(nonatomic, assign) std::string lastFocusedElementFrameIdentifier; @property(nonatomic, assign) std::string lastFocusedElementFrameIdentifier;
...@@ -60,24 +67,46 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1; ...@@ -60,24 +67,46 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
// The last seen focused element identifier. // The last seen focused element identifier.
@property(nonatomic, assign) std::string lastFocusedElementIdentifier; @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 @end
@implementation ManualFillInjectionHandler @implementation ManualFillInjectionHandler
- (instancetype)initWithWebStateList:(WebStateList*)webStateList { - (instancetype)initWithWebStateList:(WebStateList*)webStateList
securityAlertPresenter:
(id<AutofillSecurityAlertPresenter>)securityAlertPresenter {
self = [super init]; self = [super init];
if (self) { if (self) {
_webStateList = webStateList; _webStateList = webStateList;
_alertPresenter = securityAlertPresenter;
_formHelper = _formHelper =
[[FormObserverHelper alloc] initWithWebStateList:webStateList]; [[FormObserverHelper alloc] initWithWebStateList:webStateList];
_formHelper.delegate = self; _formHelper.delegate = self;
} }
return self; return self;
} }
#pragma mark - ManualFillViewDelegate
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure { #pragma mark - ManualFillContentDelegate
if (isSecure && !self.lastFocusedElementIsSecure) {
- (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; return;
} }
[self fillLastSelectedFieldWithString:content]; [self fillLastSelectedFieldWithString:content];
...@@ -91,17 +120,16 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1; ...@@ -91,17 +120,16 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
if (params.type != "focus") { if (params.type != "focus") {
return; return;
} }
BOOL isContextSecure = autofill::IsContextSecureForWebState(webState); self.lastFocusedElementSecure =
BOOL isPasswordField = params.field_type == "password"; autofill::IsContextSecureForWebState(webState);
self.lastFocusedElementIsSecure = isContextSecure && isPasswordField; self.lastFocusedElementPasswordField = params.field_type == "password";
self.lastFocusedElementIdentifier = params.field_identifier; self.lastFocusedElementIdentifier = params.field_identifier;
if (autofill::switches::IsAutofillIFrameMessagingEnabled()) { if (autofill::switches::IsAutofillIFrameMessagingEnabled()) {
DCHECK(frame); DCHECK(frame);
self.lastFocusedElementFrameIdentifier = frame->GetFrameId(); self.lastFocusedElementFrameIdentifier = frame->GetFrameId();
const GURL frameSecureOrigin = frame->GetSecurityOrigin(); const GURL frameSecureOrigin = frame->GetSecurityOrigin();
if (!frameSecureOrigin.SchemeIsCryptographic()) { if (!frameSecureOrigin.SchemeIsCryptographic()) {
self.lastFocusedElementIsSecure = NO; self.lastFocusedElementSecure = NO;
} }
} }
} }
...@@ -126,7 +154,7 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1; ...@@ -126,7 +154,7 @@ const int64_t kJavaScriptExecutionTimeoutInSeconds = 1;
return manager; return manager;
} }
#pragma mark - Document Interaction #pragma mark - Private
// Injects the passed string to the active field and jumps to the next field. // Injects the passed string to the active field and jumps to the next field.
- (void)fillLastSelectedFieldWithString:(NSString*)string { - (void)fillLastSelectedFieldWithString:(NSString*)string {
......
...@@ -185,14 +185,16 @@ static const CGFloat sideMargins = 16; ...@@ -185,14 +185,16 @@ static const CGFloat sideMargins = 16;
base::RecordAction( base::RecordAction(
base::UserMetricsAction("ManualFallback_Password_SelectUsername")); base::UserMetricsAction("ManualFallback_Password_SelectUsername"));
[self.delegate userDidPickContent:self.manualFillCredential.username [self.delegate userDidPickContent:self.manualFillCredential.username
isSecure:NO]; isPasswordField:NO
requiresHTTPS:NO];
} }
- (void)userDidTapPasswordButton:(UIButton*)button { - (void)userDidTapPasswordButton:(UIButton*)button {
base::RecordAction( base::RecordAction(
base::UserMetricsAction("ManualFallback_Password_SelectPassword")); base::UserMetricsAction("ManualFallback_Password_SelectPassword"));
[self.delegate userDidPickContent:self.manualFillCredential.password [self.delegate userDidPickContent:self.manualFillCredential.password
isSecure:YES]; isPasswordField:YES
requiresHTTPS:YES];
} }
@end @end
...@@ -219,9 +219,13 @@ NSString* const OtherPasswordsAccessibilityIdentifier = ...@@ -219,9 +219,13 @@ NSString* const OtherPasswordsAccessibilityIdentifier =
#pragma mark - ManualFillContentDelegate #pragma mark - ManualFillContentDelegate
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure { - (void)userDidPickContent:(NSString*)content
isPasswordField:(BOOL)isPasswordField
requiresHTTPS:(BOOL)requiresHTTPS {
[self.navigationDelegate dismissPresentedViewController]; [self.navigationDelegate dismissPresentedViewController];
[self.contentDelegate userDidPickContent:content isSecure:isSecure]; [self.contentDelegate userDidPickContent:content
isPasswordField:isPasswordField
requiresHTTPS:requiresHTTPS];
} }
@end @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