| // Copyright 2017 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. |
| |
| #import "ios/web_view/shell/shell_autofill_delegate.h" |
| |
| #import <UIKit/UIKit.h> |
| |
| #import "ios/web_view/shell/shell_risk_data_loader.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| @interface ShellAutofillDelegate () |
| |
| // Autofill controller. |
| @property(nonatomic, strong) CWVAutofillController* autofillController; |
| |
| // Risk data loader. |
| @property(nonatomic, strong) ShellRiskDataLoader* riskDataLoader; |
| |
| // Returns an action for a suggestion. |
| - (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion; |
| |
| @end |
| |
| @implementation ShellAutofillDelegate |
| |
| @synthesize autofillController = _autofillController; |
| @synthesize riskDataLoader = _riskDataLoader; |
| |
| - (instancetype)init { |
| self = [super init]; |
| if (self) { |
| _riskDataLoader = [[ShellRiskDataLoader alloc] init]; |
| } |
| return self; |
| } |
| |
| #pragma mark - CWVAutofillControllerDelegate methods |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| didFocusOnFieldWithIdentifier:(NSString*)fieldIdentifier |
| fieldType:(NSString*)fieldType |
| formName:(NSString*)formName |
| frameID:(NSString*)frameID |
| value:(NSString*)value |
| userInitiated:(BOOL)userInitiated { |
| _autofillController = autofillController; |
| |
| __weak ShellAutofillDelegate* weakSelf = self; |
| id completionHandler = ^(NSArray<CWVAutofillSuggestion*>* suggestions) { |
| ShellAutofillDelegate* strongSelf = weakSelf; |
| if (!suggestions.count || !strongSelf) { |
| return; |
| } |
| |
| UIAlertController* alertController = [UIAlertController |
| alertControllerWithTitle:@"Pick a suggestion" |
| message:nil |
| preferredStyle:UIAlertControllerStyleActionSheet]; |
| alertController.popoverPresentationController.sourceView = |
| UIApplication.sharedApplication.keyWindow; |
| CGRect bounds = UIApplication.sharedApplication.keyWindow.bounds; |
| alertController.popoverPresentationController.sourceRect = |
| CGRectMake(CGRectGetWidth(bounds) / 2, 60, 1, 1); |
| UIAlertAction* cancelAction = |
| [UIAlertAction actionWithTitle:@"Cancel" |
| style:UIAlertActionStyleCancel |
| handler:nil]; |
| [alertController addAction:cancelAction]; |
| for (CWVAutofillSuggestion* suggestion in suggestions) { |
| [alertController addAction:[self actionForSuggestion:suggestion]]; |
| } |
| |
| [UIApplication.sharedApplication.keyWindow.rootViewController |
| presentViewController:alertController |
| animated:YES |
| completion:nil]; |
| }; |
| [autofillController fetchSuggestionsForFormWithName:formName |
| fieldIdentifier:fieldIdentifier |
| fieldType:fieldType |
| frameID:frameID |
| completionHandler:completionHandler]; |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| didInputInFieldWithIdentifier:(NSString*)fieldIdentifier |
| fieldType:(NSString*)fieldType |
| formName:(NSString*)formName |
| frameID:(NSString*)frameID |
| value:(NSString*)value |
| userInitiated:(BOOL)userInitiated { |
| // Not implemented. |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| didBlurOnFieldWithIdentifier:(NSString*)fieldIdentifier |
| fieldType:(NSString*)fieldType |
| formName:(NSString*)formName |
| frameID:(NSString*)frameID |
| value:(NSString*)value |
| userInitiated:(BOOL)userInitiated { |
| // Not implemented. |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| didSubmitFormWithName:(NSString*)formName |
| frameID:(NSString*)frameID |
| userInitiated:(BOOL)userInitiated { |
| // Not implemented. |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| didFindForms:(NSArray<CWVAutofillForm*>*)forms |
| frameID:(NSString*)frameID { |
| if (forms.count == 0) { |
| return; |
| } |
| |
| NSArray<NSString*>* debugDescriptions = |
| [forms valueForKey:NSStringFromSelector(@selector(debugDescription))]; |
| NSLog(@"Found forms in frame %@\n%@", frameID, debugDescriptions); |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| saveCreditCardWithSaver:(CWVCreditCardSaver*)saver { |
| CWVCreditCard* creditCard = saver.creditCard; |
| UIAlertController* alertController = |
| [UIAlertController alertControllerWithTitle:@"Save card?" |
| message:creditCard.debugDescription |
| preferredStyle:UIAlertControllerStyleAlert]; |
| __weak UIAlertController* weakAlertController = alertController; |
| __weak ShellAutofillDelegate* weakSelf = self; |
| UIAlertAction* allowAction = [UIAlertAction |
| actionWithTitle:@"Allow" |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* _Nonnull action) { |
| NSString* cardHolderFullName = |
| weakAlertController.textFields[0].text; |
| NSString* expirationMonth = |
| weakAlertController.textFields[1].text; |
| NSString* expirationYear = |
| weakAlertController.textFields[2].text; |
| [saver acceptWithCardHolderFullName:cardHolderFullName |
| expirationMonth:expirationMonth |
| expirationYear:expirationYear |
| riskData:weakSelf.riskDataLoader |
| .riskData |
| completionHandler:^(BOOL cardSaved) { |
| if (!cardSaved) { |
| NSLog(@"Failed to save: %@", |
| saver.creditCard); |
| } |
| }]; |
| }]; |
| UIAlertAction* cancelAction = |
| [UIAlertAction actionWithTitle:@"Cancel" |
| style:UIAlertActionStyleCancel |
| handler:^(UIAlertAction* _Nonnull action) { |
| [saver decline]; |
| }]; |
| [alertController addAction:allowAction]; |
| [alertController addAction:cancelAction]; |
| |
| [alertController |
| addTextFieldWithConfigurationHandler:^(UITextField* textField) { |
| textField.placeholder = @"Card holder full name"; |
| textField.keyboardType = UIKeyboardTypeDefault; |
| }]; |
| [alertController |
| addTextFieldWithConfigurationHandler:^(UITextField* textField) { |
| textField.placeholder = @"Expiration month (MM)"; |
| textField.keyboardType = UIKeyboardTypeNumberPad; |
| }]; |
| [alertController |
| addTextFieldWithConfigurationHandler:^(UITextField* textField) { |
| textField.placeholder = @"Expiration year (YYYY)"; |
| textField.keyboardType = UIKeyboardTypeNumberPad; |
| }]; |
| |
| [UIApplication.sharedApplication.keyWindow.rootViewController |
| presentViewController:alertController |
| animated:YES |
| completion:nil]; |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| decideSavePolicyForPassword:(CWVPassword*)password |
| decisionHandler:(void (^)(CWVPasswordUserDecision decision)) |
| decisionHandler { |
| UIAlertController* alertController = |
| [UIAlertController alertControllerWithTitle:@"Save password?" |
| message:password.debugDescription |
| preferredStyle:UIAlertControllerStyleAlert]; |
| |
| UIAlertAction* noAction = [UIAlertAction |
| actionWithTitle:@"Not this time" |
| style:UIAlertActionStyleCancel |
| handler:^(UIAlertAction* _Nonnull action) { |
| decisionHandler(CWVPasswordUserDecisionNotThisTime); |
| }]; |
| [alertController addAction:noAction]; |
| |
| UIAlertAction* neverAction = |
| [UIAlertAction actionWithTitle:@"Never" |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* _Nonnull action) { |
| decisionHandler(CWVPasswordUserDecisionNever); |
| }]; |
| [alertController addAction:neverAction]; |
| |
| UIAlertAction* yesAction = |
| [UIAlertAction actionWithTitle:@"Save" |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* _Nonnull action) { |
| decisionHandler(CWVPasswordUserDecisionYes); |
| }]; |
| [alertController addAction:yesAction]; |
| |
| [UIApplication.sharedApplication.keyWindow.rootViewController |
| presentViewController:alertController |
| animated:YES |
| completion:nil]; |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| decideUpdatePolicyForPassword:(CWVPassword*)password |
| decisionHandler:(void (^)(CWVPasswordUserDecision decision)) |
| decisionHandler { |
| UIAlertController* alertController = |
| [UIAlertController alertControllerWithTitle:@"Update password?" |
| message:password.debugDescription |
| preferredStyle:UIAlertControllerStyleAlert]; |
| |
| UIAlertAction* noAction = [UIAlertAction |
| actionWithTitle:@"Not this time" |
| style:UIAlertActionStyleCancel |
| handler:^(UIAlertAction* _Nonnull action) { |
| decisionHandler(CWVPasswordUserDecisionNotThisTime); |
| }]; |
| [alertController addAction:noAction]; |
| |
| UIAlertAction* yesAction = |
| [UIAlertAction actionWithTitle:@"Update" |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* _Nonnull action) { |
| decisionHandler(CWVPasswordUserDecisionYes); |
| }]; |
| [alertController addAction:yesAction]; |
| |
| [UIApplication.sharedApplication.keyWindow.rootViewController |
| presentViewController:alertController |
| animated:YES |
| completion:nil]; |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| verifyCreditCardWithVerifier:(CWVCreditCardVerifier*)verifier { |
| [UIApplication.sharedApplication.keyWindow endEditing:YES]; |
| |
| UIAlertController* alertController = |
| [UIAlertController alertControllerWithTitle:@"Verify Card" |
| message:@"Enter CVC" |
| preferredStyle:UIAlertControllerStyleAlert]; |
| |
| __weak UIAlertController* weakAlertController = alertController; |
| __weak ShellAutofillDelegate* weakSelf = self; |
| UIAlertAction* submit = [UIAlertAction |
| actionWithTitle:@"Confirm" |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* action) { |
| UITextField* textField = |
| weakAlertController.textFields.firstObject; |
| NSString* CVC = textField.text; |
| [verifier verifyWithCVC:CVC |
| expirationMonth:nil |
| expirationYear:nil |
| riskData:weakSelf.riskDataLoader.riskData |
| completionHandler:^(NSError* error) { |
| if (error) { |
| NSLog(@"Card %@ failed to verify error: %@", |
| verifier.creditCard, error); |
| } |
| }]; |
| }]; |
| |
| [alertController addAction:submit]; |
| |
| UIAlertAction* cancel = |
| [UIAlertAction actionWithTitle:@"Cancel" |
| style:UIAlertActionStyleCancel |
| handler:nil]; |
| [alertController addAction:cancel]; |
| |
| [alertController |
| addTextFieldWithConfigurationHandler:^(UITextField* textField) { |
| textField.placeholder = @"CVC"; |
| textField.keyboardType = UIKeyboardTypeNumberPad; |
| }]; |
| |
| [UIApplication.sharedApplication.keyWindow.rootViewController |
| presentViewController:alertController |
| animated:YES |
| completion:nil]; |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| notifyUserOfPasswordLeakOnURL:(NSURL*)URL |
| leakType:(CWVPasswordLeakType)leakType { |
| NSLog(@"Password on %@ is leaked!", URL); |
| } |
| |
| - (void)autofillController:(CWVAutofillController*)autofillController |
| suggestGeneratedPassword:(NSString*)generatedPassword |
| decisionHandler:(void (^)(BOOL accept))decisionHandler { |
| NSLog(@"Accepting suggested password: %@", generatedPassword); |
| decisionHandler(YES); |
| } |
| |
| #pragma mark - Private Methods |
| |
| - (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion { |
| NSString* title = |
| [NSString stringWithFormat:@"%@ %@", suggestion.value, |
| suggestion.displayDescription ?: @""]; |
| __weak ShellAutofillDelegate* weakSelf = self; |
| return [UIAlertAction |
| actionWithTitle:title |
| style:UIAlertActionStyleDefault |
| handler:^(UIAlertAction* action) { |
| ShellAutofillDelegate* strongSelf = weakSelf; |
| if (!strongSelf) { |
| return; |
| } |
| [strongSelf.autofillController acceptSuggestion:suggestion |
| completionHandler:nil]; |
| [UIApplication.sharedApplication.keyWindow endEditing:YES]; |
| }]; |
| } |
| |
| @end |