shell_autofill_delegate.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "ios/web_view/shell/shell_autofill_delegate.h"
  5. #import <UIKit/UIKit.h>
  6. #import "ios/web_view/shell/shell_risk_data_loader.h"
  7. #if !defined(__has_feature) || !__has_feature(objc_arc)
  8. #error "This file requires ARC support."
  9. #endif
  10. @interface ShellAutofillDelegate ()
  11. // Autofill controller.
  12. @property(nonatomic, strong) CWVAutofillController* autofillController;
  13. // Risk data loader.
  14. @property(nonatomic, strong) ShellRiskDataLoader* riskDataLoader;
  15. // Returns an action for a suggestion.
  16. - (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion;
  17. @end
  18. @implementation ShellAutofillDelegate
  19. @synthesize autofillController = _autofillController;
  20. @synthesize riskDataLoader = _riskDataLoader;
  21. - (instancetype)init {
  22. self = [super init];
  23. if (self) {
  24. _riskDataLoader = [[ShellRiskDataLoader alloc] init];
  25. }
  26. return self;
  27. }
  28. #pragma mark - CWVAutofillControllerDelegate methods
  29. - (void)autofillController:(CWVAutofillController*)autofillController
  30. didFocusOnFieldWithIdentifier:(NSString*)fieldIdentifier
  31. fieldType:(NSString*)fieldType
  32. formName:(NSString*)formName
  33. frameID:(NSString*)frameID
  34. value:(NSString*)value
  35. userInitiated:(BOOL)userInitiated {
  36. _autofillController = autofillController;
  37. __weak ShellAutofillDelegate* weakSelf = self;
  38. id completionHandler = ^(NSArray<CWVAutofillSuggestion*>* suggestions) {
  39. ShellAutofillDelegate* strongSelf = weakSelf;
  40. if (!suggestions.count || !strongSelf) {
  41. return;
  42. }
  43. UIAlertController* alertController = [UIAlertController
  44. alertControllerWithTitle:@"Pick a suggestion"
  45. message:nil
  46. preferredStyle:UIAlertControllerStyleActionSheet];
  47. alertController.popoverPresentationController.sourceView =
  48. [self anyKeyWindow];
  49. CGRect bounds = [self anyKeyWindow].bounds;
  50. alertController.popoverPresentationController.sourceRect =
  51. CGRectMake(CGRectGetWidth(bounds) / 2, 60, 1, 1);
  52. UIAlertAction* cancelAction =
  53. [UIAlertAction actionWithTitle:@"Cancel"
  54. style:UIAlertActionStyleCancel
  55. handler:nil];
  56. [alertController addAction:cancelAction];
  57. for (CWVAutofillSuggestion* suggestion in suggestions) {
  58. [alertController addAction:[self actionForSuggestion:suggestion]];
  59. }
  60. [[self anyKeyWindow].rootViewController
  61. presentViewController:alertController
  62. animated:YES
  63. completion:nil];
  64. };
  65. [autofillController fetchSuggestionsForFormWithName:formName
  66. fieldIdentifier:fieldIdentifier
  67. fieldType:fieldType
  68. frameID:frameID
  69. completionHandler:completionHandler];
  70. }
  71. - (void)autofillController:(CWVAutofillController*)autofillController
  72. didInputInFieldWithIdentifier:(NSString*)fieldIdentifier
  73. fieldType:(NSString*)fieldType
  74. formName:(NSString*)formName
  75. frameID:(NSString*)frameID
  76. value:(NSString*)value
  77. userInitiated:(BOOL)userInitiated {
  78. // TODO(crbug.com/1323932): Fetching suggestions has an important side effect
  79. // of calling PasswordFormManager::UpdateStateOnUserInput. This will ensure
  80. // that the typed information can be remembered during the save dialogue.
  81. // Make this method a no-op once the bug is fixed.
  82. id completionHandler = ^(NSArray<CWVAutofillSuggestion*>* suggestions) {
  83. NSLog(@"%@ suggestions: %@", NSStringFromSelector(_cmd), suggestions);
  84. };
  85. [autofillController fetchSuggestionsForFormWithName:formName
  86. fieldIdentifier:fieldIdentifier
  87. fieldType:fieldType
  88. frameID:frameID
  89. completionHandler:completionHandler];
  90. }
  91. - (void)autofillController:(CWVAutofillController*)autofillController
  92. didBlurOnFieldWithIdentifier:(NSString*)fieldIdentifier
  93. fieldType:(NSString*)fieldType
  94. formName:(NSString*)formName
  95. frameID:(NSString*)frameID
  96. value:(NSString*)value
  97. userInitiated:(BOOL)userInitiated {
  98. // Not implemented.
  99. }
  100. - (void)autofillController:(CWVAutofillController*)autofillController
  101. didSubmitFormWithName:(NSString*)formName
  102. frameID:(NSString*)frameID
  103. userInitiated:(BOOL)userInitiated {
  104. // Not implemented.
  105. }
  106. - (void)autofillController:(CWVAutofillController*)autofillController
  107. didFindForms:(NSArray<CWVAutofillForm*>*)forms
  108. frameID:(NSString*)frameID {
  109. if (forms.count == 0) {
  110. return;
  111. }
  112. NSArray<NSString*>* debugDescriptions =
  113. [forms valueForKey:NSStringFromSelector(@selector(debugDescription))];
  114. NSLog(@"Found forms in frame %@\n%@", frameID, debugDescriptions);
  115. }
  116. - (void)autofillController:(CWVAutofillController*)autofillController
  117. saveCreditCardWithSaver:(CWVCreditCardSaver*)saver {
  118. CWVCreditCard* creditCard = saver.creditCard;
  119. UIAlertController* alertController =
  120. [UIAlertController alertControllerWithTitle:@"Save card?"
  121. message:creditCard.debugDescription
  122. preferredStyle:UIAlertControllerStyleAlert];
  123. __weak UIAlertController* weakAlertController = alertController;
  124. __weak ShellAutofillDelegate* weakSelf = self;
  125. UIAlertAction* allowAction = [UIAlertAction
  126. actionWithTitle:@"Allow"
  127. style:UIAlertActionStyleDefault
  128. handler:^(UIAlertAction* _Nonnull action) {
  129. NSString* cardHolderFullName =
  130. weakAlertController.textFields[0].text;
  131. NSString* expirationMonth =
  132. weakAlertController.textFields[1].text;
  133. NSString* expirationYear =
  134. weakAlertController.textFields[2].text;
  135. [saver acceptWithCardHolderFullName:cardHolderFullName
  136. expirationMonth:expirationMonth
  137. expirationYear:expirationYear
  138. riskData:weakSelf.riskDataLoader
  139. .riskData
  140. completionHandler:^(BOOL cardSaved) {
  141. if (!cardSaved) {
  142. NSLog(@"Failed to save: %@",
  143. saver.creditCard);
  144. }
  145. }];
  146. }];
  147. UIAlertAction* cancelAction =
  148. [UIAlertAction actionWithTitle:@"Cancel"
  149. style:UIAlertActionStyleCancel
  150. handler:^(UIAlertAction* _Nonnull action) {
  151. [saver decline];
  152. }];
  153. [alertController addAction:allowAction];
  154. [alertController addAction:cancelAction];
  155. [alertController
  156. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  157. textField.placeholder = @"Card holder full name";
  158. textField.keyboardType = UIKeyboardTypeDefault;
  159. }];
  160. [alertController
  161. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  162. textField.placeholder = @"Expiration month (MM)";
  163. textField.keyboardType = UIKeyboardTypeNumberPad;
  164. }];
  165. [alertController
  166. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  167. textField.placeholder = @"Expiration year (YYYY)";
  168. textField.keyboardType = UIKeyboardTypeNumberPad;
  169. }];
  170. [[self anyKeyWindow].rootViewController presentViewController:alertController
  171. animated:YES
  172. completion:nil];
  173. }
  174. - (void)autofillController:(CWVAutofillController*)autofillController
  175. decideSavePolicyForPassword:(CWVPassword*)password
  176. decisionHandler:(void (^)(CWVPasswordUserDecision decision))
  177. decisionHandler {
  178. UIAlertController* alertController =
  179. [UIAlertController alertControllerWithTitle:@"Save password?"
  180. message:password.debugDescription
  181. preferredStyle:UIAlertControllerStyleAlert];
  182. UIAlertAction* noAction = [UIAlertAction
  183. actionWithTitle:@"Not this time"
  184. style:UIAlertActionStyleCancel
  185. handler:^(UIAlertAction* _Nonnull action) {
  186. decisionHandler(CWVPasswordUserDecisionNotThisTime);
  187. }];
  188. [alertController addAction:noAction];
  189. UIAlertAction* neverAction =
  190. [UIAlertAction actionWithTitle:@"Never"
  191. style:UIAlertActionStyleDefault
  192. handler:^(UIAlertAction* _Nonnull action) {
  193. decisionHandler(CWVPasswordUserDecisionNever);
  194. }];
  195. [alertController addAction:neverAction];
  196. UIAlertAction* yesAction =
  197. [UIAlertAction actionWithTitle:@"Save"
  198. style:UIAlertActionStyleDefault
  199. handler:^(UIAlertAction* _Nonnull action) {
  200. decisionHandler(CWVPasswordUserDecisionYes);
  201. }];
  202. [alertController addAction:yesAction];
  203. [[self anyKeyWindow].rootViewController presentViewController:alertController
  204. animated:YES
  205. completion:nil];
  206. }
  207. - (void)autofillController:(CWVAutofillController*)autofillController
  208. decideUpdatePolicyForPassword:(CWVPassword*)password
  209. decisionHandler:(void (^)(CWVPasswordUserDecision decision))
  210. decisionHandler {
  211. UIAlertController* alertController =
  212. [UIAlertController alertControllerWithTitle:@"Update password?"
  213. message:password.debugDescription
  214. preferredStyle:UIAlertControllerStyleAlert];
  215. UIAlertAction* noAction = [UIAlertAction
  216. actionWithTitle:@"Not this time"
  217. style:UIAlertActionStyleCancel
  218. handler:^(UIAlertAction* _Nonnull action) {
  219. decisionHandler(CWVPasswordUserDecisionNotThisTime);
  220. }];
  221. [alertController addAction:noAction];
  222. UIAlertAction* yesAction =
  223. [UIAlertAction actionWithTitle:@"Update"
  224. style:UIAlertActionStyleDefault
  225. handler:^(UIAlertAction* _Nonnull action) {
  226. decisionHandler(CWVPasswordUserDecisionYes);
  227. }];
  228. [alertController addAction:yesAction];
  229. [[self anyKeyWindow].rootViewController presentViewController:alertController
  230. animated:YES
  231. completion:nil];
  232. }
  233. - (void)autofillController:(CWVAutofillController*)autofillController
  234. verifyCreditCardWithVerifier:(CWVCreditCardVerifier*)verifier {
  235. [[self anyKeyWindow] endEditing:YES];
  236. UIAlertController* alertController =
  237. [UIAlertController alertControllerWithTitle:@"Verify Card"
  238. message:@"Enter CVC"
  239. preferredStyle:UIAlertControllerStyleAlert];
  240. __weak UIAlertController* weakAlertController = alertController;
  241. __weak ShellAutofillDelegate* weakSelf = self;
  242. UIAlertAction* submit = [UIAlertAction
  243. actionWithTitle:@"Confirm"
  244. style:UIAlertActionStyleDefault
  245. handler:^(UIAlertAction* action) {
  246. UITextField* textField =
  247. weakAlertController.textFields.firstObject;
  248. NSString* CVC = textField.text;
  249. [verifier verifyWithCVC:CVC
  250. expirationMonth:nil
  251. expirationYear:nil
  252. riskData:weakSelf.riskDataLoader.riskData
  253. completionHandler:^(NSError* error) {
  254. if (error) {
  255. NSLog(@"Card %@ failed to verify error: %@",
  256. verifier.creditCard, error);
  257. }
  258. }];
  259. }];
  260. [alertController addAction:submit];
  261. UIAlertAction* cancel =
  262. [UIAlertAction actionWithTitle:@"Cancel"
  263. style:UIAlertActionStyleCancel
  264. handler:nil];
  265. [alertController addAction:cancel];
  266. [alertController
  267. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  268. textField.placeholder = @"CVC";
  269. textField.keyboardType = UIKeyboardTypeNumberPad;
  270. }];
  271. [[self anyKeyWindow].rootViewController presentViewController:alertController
  272. animated:YES
  273. completion:nil];
  274. }
  275. - (void)autofillController:(CWVAutofillController*)autofillController
  276. notifyUserOfPasswordLeakOnURL:(NSURL*)URL
  277. leakType:(CWVPasswordLeakType)leakType
  278. username:(NSString*)username {
  279. NSLog(@"Password on %@ is leaked for username %@!", URL, username);
  280. }
  281. - (void)autofillController:(CWVAutofillController*)autofillController
  282. suggestGeneratedPassword:(NSString*)generatedPassword
  283. decisionHandler:(void (^)(BOOL accept))decisionHandler {
  284. NSLog(@"Accepting suggested password: %@", generatedPassword);
  285. decisionHandler(YES);
  286. }
  287. - (void)autofillController:(CWVAutofillController*)autofillController
  288. confirmSaveForNewAutofillProfile:(CWVAutofillProfile*)newProfile
  289. oldProfile:(nullable CWVAutofillProfile*)oldProfile
  290. decisionHandler:
  291. (void (^)(CWVAutofillProfileUserDecision decision))
  292. decisionHandler {
  293. NSString* message =
  294. [NSString stringWithFormat:@"new: %@\nold: %@",
  295. newProfile.debugDescription, oldProfile];
  296. UIAlertController* alertController = [UIAlertController
  297. alertControllerWithTitle:@"Confirm save for new profile?"
  298. message:message
  299. preferredStyle:UIAlertControllerStyleAlert];
  300. UIAlertAction* accept = [UIAlertAction
  301. actionWithTitle:@"Accept"
  302. style:UIAlertActionStyleDefault
  303. handler:^(UIAlertAction* action) {
  304. decisionHandler(CWVAutofillProfileUserDecisionAccepted);
  305. }];
  306. [alertController addAction:accept];
  307. UIAlertAction* decline = [UIAlertAction
  308. actionWithTitle:@"Decline"
  309. style:UIAlertActionStyleCancel
  310. handler:^(UIAlertAction* action) {
  311. decisionHandler(CWVAutofillProfileUserDecisionDeclined);
  312. }];
  313. [alertController addAction:decline];
  314. [[self anyKeyWindow].rootViewController presentViewController:alertController
  315. animated:YES
  316. completion:nil];
  317. }
  318. #pragma mark - Private Methods
  319. - (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion {
  320. NSString* title =
  321. [NSString stringWithFormat:@"%@ %@", suggestion.value,
  322. suggestion.displayDescription ?: @""];
  323. __weak ShellAutofillDelegate* weakSelf = self;
  324. return [UIAlertAction
  325. actionWithTitle:title
  326. style:UIAlertActionStyleDefault
  327. handler:^(UIAlertAction* action) {
  328. ShellAutofillDelegate* strongSelf = weakSelf;
  329. if (!strongSelf) {
  330. return;
  331. }
  332. [strongSelf.autofillController acceptSuggestion:suggestion
  333. completionHandler:nil];
  334. [[self anyKeyWindow] endEditing:YES];
  335. }];
  336. }
  337. #pragma mark - Private
  338. - (UIWindow*)anyKeyWindow {
  339. NSArray<UIWindow*>* windows = [UIApplication sharedApplication].windows;
  340. for (UIWindow* window in windows) {
  341. if (window.isKeyWindow)
  342. return window;
  343. }
  344. return nil;
  345. }
  346. @end