1// Copyright 2014 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 5#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.h" 6 7#include <vector> 8 9#include "base/ios/ios_util.h" 10#include "base/mac/foundation_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "components/autofill/core/browser/personal_data_manager.h" 13#include "components/autofill/core/common/autofill_features.h" 14#import "components/autofill/ios/browser/js_suggestion_manager.h" 15#include "components/keyed_service/core/service_access_type.h" 16#include "components/password_manager/core/browser/password_ui_utils.h" 17#include "components/strings/grit/components_strings.h" 18#include "ios/chrome/browser/autofill/personal_data_manager_factory.h" 19#include "ios/chrome/browser/browser_state/chrome_browser_state.h" 20#import "ios/chrome/browser/main/browser.h" 21#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h" 22#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" 23#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h" 24#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.h" 25#import "ios/chrome/browser/ui/autofill/manual_fill/address_coordinator.h" 26#import "ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.h" 27#import "ios/chrome/browser/ui/autofill/manual_fill/fallback_view_controller.h" 28#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h" 29#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.h" 30#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h" 31#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h" 32#import "ios/chrome/browser/ui/commands/application_commands.h" 33#import "ios/chrome/browser/ui/commands/command_dispatcher.h" 34#import "ios/chrome/browser/ui/commands/open_new_tab_command.h" 35#import "ios/chrome/browser/ui/commands/security_alert_commands.h" 36#import "ios/chrome/browser/ui/main/scene_state.h" 37#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h" 38#include "ios/chrome/browser/ui/util/ui_util.h" 39#import "ios/chrome/browser/web_state_list/web_state_list.h" 40#import "ios/chrome/common/ui/reauthentication/reauthentication_module.h" 41#include "ios/chrome/grit/ios_strings.h" 42#import "ios/web/public/web_state.h" 43#include "ui/base/l10n/l10n_util_mac.h" 44 45#if !defined(__has_feature) || !__has_feature(objc_arc) 46#error "This file requires ARC support." 47#endif 48 49@interface FormInputAccessoryCoordinator () < 50 AddressCoordinatorDelegate, 51 CardCoordinatorDelegate, 52 FormInputAccessoryMediatorDelegate, 53 ManualFillAccessoryViewControllerDelegate, 54 PasswordCoordinatorDelegate, 55 SecurityAlertCommands> 56 57// Coordinator in charge of the presenting password autofill options as a modal. 58@property(nonatomic, strong) 59 ManualFillAllPasswordCoordinator* allPasswordCoordinator; 60 61// The Mediator for the input accessory view controller. 62@property(nonatomic, strong) 63 FormInputAccessoryMediator* formInputAccessoryMediator; 64 65// The View Controller for the input accessory view. 66@property(nonatomic, strong) 67 FormInputAccessoryViewController* formInputAccessoryViewController; 68 69// The object in charge of interacting with the web view. Used to fill the data 70// in the forms. 71@property(nonatomic, strong) ManualFillInjectionHandler* injectionHandler; 72 73// Reauthentication Module used for re-authentication. 74@property(nonatomic, strong) ReauthenticationModule* reauthenticationModule; 75 76// Modal alert. 77@property(nonatomic, strong) AlertCoordinator* alertCoordinator; 78 79@end 80 81@implementation FormInputAccessoryCoordinator 82 83- (instancetype)initWithBaseViewController:(UIViewController*)viewController 84 browser:(Browser*)browser { 85 self = [super initWithBaseViewController:viewController browser:browser]; 86 if (self) { 87 CommandDispatcher* dispatcher = browser->GetCommandDispatcher(); 88 [dispatcher startDispatchingToTarget:self 89 forProtocol:@protocol(SecurityAlertCommands)]; 90 __weak id<SecurityAlertCommands> securityAlertHandler = 91 HandlerForProtocol(dispatcher, SecurityAlertCommands); 92 _reauthenticationModule = [[ReauthenticationModule alloc] init]; 93 _injectionHandler = [[ManualFillInjectionHandler alloc] 94 initWithWebStateList:browser->GetWebStateList() 95 securityAlertHandler:securityAlertHandler 96 reauthenticationModule:_reauthenticationModule]; 97 } 98 return self; 99} 100 101- (void)start { 102 self.formInputAccessoryViewController = 103 [[FormInputAccessoryViewController alloc] 104 initWithManualFillAccessoryViewControllerDelegate:self]; 105 106 auto passwordStore = IOSChromePasswordStoreFactory::GetForBrowserState( 107 self.browser->GetBrowserState(), ServiceAccessType::EXPLICIT_ACCESS); 108 109 // There is no personal data manager in OTR (incognito). Get the original 110 // one for manual fallback. 111 autofill::PersonalDataManager* personalDataManager = 112 autofill::PersonalDataManagerFactory::GetForBrowserState( 113 self.browser->GetBrowserState()->GetOriginalChromeBrowserState()); 114 115 AppState* appState = SceneStateBrowserAgent::FromBrowser(self.browser) 116 ->GetSceneState() 117 .appState; 118 __weak id<SecurityAlertCommands> securityAlertHandler = HandlerForProtocol( 119 self.browser->GetCommandDispatcher(), SecurityAlertCommands); 120 self.formInputAccessoryMediator = [[FormInputAccessoryMediator alloc] 121 initWithConsumer:self.formInputAccessoryViewController 122 delegate:self 123 webStateList:self.browser->GetWebStateList() 124 personalDataManager:personalDataManager 125 passwordStore:passwordStore 126 appState:appState 127 securityAlertHandler:securityAlertHandler 128 reauthenticationModule:self.reauthenticationModule]; 129 self.formInputAccessoryViewController.formSuggestionClient = 130 self.formInputAccessoryMediator; 131} 132 133- (void)stop { 134 [self stopChildren]; 135 136 [self.formInputAccessoryViewController restoreOriginalKeyboardView]; 137 self.formInputAccessoryViewController = nil; 138 139 [self.formInputAccessoryMediator disconnect]; 140 self.formInputAccessoryMediator = nil; 141 142 [self.allPasswordCoordinator stop]; 143 self.allPasswordCoordinator = nil; 144} 145 146- (void)reset { 147 [self stopChildren]; 148 [self.formInputAccessoryMediator enableSuggestions]; 149 [self.formInputAccessoryViewController reset]; 150} 151 152#pragma mark - Presenting Children 153 154- (void)stopChildren { 155 for (ChromeCoordinator* coordinator in self.childCoordinators) { 156 [coordinator stop]; 157 } 158 [self.childCoordinators removeAllObjects]; 159} 160 161- (void)startPasswordsFromButton:(UIButton*)button { 162 WebStateList* webStateList = self.browser->GetWebStateList(); 163 DCHECK(webStateList->GetActiveWebState()); 164 const GURL& URL = webStateList->GetActiveWebState()->GetLastCommittedURL(); 165 ManualFillPasswordCoordinator* passwordCoordinator = 166 [[ManualFillPasswordCoordinator alloc] 167 initWithBaseViewController:self.baseViewController 168 browser:self.browser 169 URL:URL 170 injectionHandler:self.injectionHandler]; 171 passwordCoordinator.delegate = self; 172 if (IsIPadIdiom()) { 173 [passwordCoordinator presentFromButton:button]; 174 } else { 175 [self.formInputAccessoryViewController 176 presentView:passwordCoordinator.viewController.view]; 177 } 178 179 [self.childCoordinators addObject:passwordCoordinator]; 180} 181 182- (void)startCardsFromButton:(UIButton*)button { 183 CardCoordinator* cardCoordinator = [[CardCoordinator alloc] 184 initWithBaseViewController:self.baseViewController 185 browser:self.browser 186 injectionHandler:self.injectionHandler]; 187 cardCoordinator.delegate = self; 188 if (IsIPadIdiom()) { 189 [cardCoordinator presentFromButton:button]; 190 } else { 191 [self.formInputAccessoryViewController 192 presentView:cardCoordinator.viewController.view]; 193 } 194 195 [self.childCoordinators addObject:cardCoordinator]; 196} 197 198- (void)startAddressFromButton:(UIButton*)button { 199 AddressCoordinator* addressCoordinator = [[AddressCoordinator alloc] 200 initWithBaseViewController:self.baseViewController 201 browser:self.browser 202 injectionHandler:self.injectionHandler]; 203 addressCoordinator.delegate = self; 204 if (IsIPadIdiom()) { 205 [addressCoordinator presentFromButton:button]; 206 } else { 207 [self.formInputAccessoryViewController 208 presentView:addressCoordinator.viewController.view]; 209 } 210 211 [self.childCoordinators addObject:addressCoordinator]; 212} 213 214#pragma mark - FormInputAccessoryMediatorDelegate 215 216- (void)mediatorDidDetectKeyboardHide:(FormInputAccessoryMediator*)mediator { 217 // On iOS 13, beta 3, the popover is not dismissed when the keyboard hides. 218 // This explicitly dismiss any popover. 219 // TODO(crbug.com/1116037): Verify if this workaround is still needed. 220 if (base::ios::IsRunningOnIOS13OrLater() && IsIPadIdiom()) { 221 [self reset]; 222 } 223} 224 225- (void)mediatorDidDetectMovingToBackground: 226 (FormInputAccessoryMediator*)mediator { 227 [self reset]; 228} 229 230#pragma mark - ManualFillAccessoryViewControllerDelegate 231 232- (void)keyboardButtonPressed { 233 [self reset]; 234} 235 236- (void)accountButtonPressed:(UIButton*)sender { 237 [self stopChildren]; 238 [self startAddressFromButton:sender]; 239 [self.formInputAccessoryViewController lockManualFallbackView]; 240 [self.formInputAccessoryMediator disableSuggestions]; 241} 242 243- (void)cardButtonPressed:(UIButton*)sender { 244 [self stopChildren]; 245 [self startCardsFromButton:sender]; 246 [self.formInputAccessoryViewController lockManualFallbackView]; 247 [self.formInputAccessoryMediator disableSuggestions]; 248} 249 250- (void)passwordButtonPressed:(UIButton*)sender { 251 [self stopChildren]; 252 [self startPasswordsFromButton:sender]; 253 [self.formInputAccessoryViewController lockManualFallbackView]; 254 [self.formInputAccessoryMediator disableSuggestions]; 255} 256 257#pragma mark - FallbackCoordinatorDelegate 258 259- (void)fallbackCoordinatorDidDismissPopover: 260 (FallbackCoordinator*)fallbackCoordinator { 261 [self reset]; 262} 263 264#pragma mark - PasswordCoordinatorDelegate 265 266- (void)openPasswordSettings { 267 [self reset]; 268 [self.navigator openPasswordSettings]; 269} 270 271- (void)openAllPasswordsPicker { 272 [self showConfirmationDialogToUseOtherPassword]; 273} 274 275#pragma mark - CardCoordinatorDelegate 276 277- (void)openCardSettings { 278 [self reset]; 279 [self.navigator openCreditCardSettings]; 280} 281 282#pragma mark - AddressCoordinatorDelegate 283 284- (void)openAddressSettings { 285 [self reset]; 286 [self.navigator openAddressSettings]; 287} 288 289#pragma mark - SecurityAlertCommands 290 291- (void)presentSecurityWarningAlertWithText:(NSString*)body { 292 NSString* alertTitle = 293 l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_TITLE); 294 NSString* defaultActionTitle = 295 l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_OK_BUTTON); 296 297 UIAlertController* alert = 298 [UIAlertController alertControllerWithTitle:alertTitle 299 message:body 300 preferredStyle:UIAlertControllerStyleAlert]; 301 UIAlertAction* defaultAction = 302 [UIAlertAction actionWithTitle:defaultActionTitle 303 style:UIAlertActionStyleDefault 304 handler:^(UIAlertAction* action){ 305 }]; 306 [alert addAction:defaultAction]; 307 UIViewController* presenter = self.baseViewController; 308 while (presenter.presentedViewController) { 309 presenter = presenter.presentedViewController; 310 } 311 [presenter presentViewController:alert animated:YES completion:nil]; 312} 313 314- (void)showSetPasscodeDialog { 315 UIAlertController* alertController = [UIAlertController 316 alertControllerWithTitle:l10n_util::GetNSString( 317 IDS_IOS_SETTINGS_SET_UP_SCREENLOCK_TITLE) 318 message:l10n_util::GetNSString( 319 IDS_IOS_AUTOFILL_SET_UP_SCREENLOCK_CONTENT) 320 preferredStyle:UIAlertControllerStyleAlert]; 321 322 __weak id<ApplicationCommands> applicationCommandsHandler = 323 HandlerForProtocol(self.browser->GetCommandDispatcher(), 324 ApplicationCommands); 325 OpenNewTabCommand* command = 326 [OpenNewTabCommand commandWithURLFromChrome:GURL(kPasscodeArticleURL)]; 327 328 UIAlertAction* learnAction = [UIAlertAction 329 actionWithTitle:l10n_util::GetNSString( 330 IDS_IOS_SETTINGS_SET_UP_SCREENLOCK_LEARN_HOW) 331 style:UIAlertActionStyleDefault 332 handler:^(UIAlertAction*) { 333 [applicationCommandsHandler openURLInNewTab:command]; 334 }]; 335 [alertController addAction:learnAction]; 336 UIAlertAction* okAction = 337 [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_OK) 338 style:UIAlertActionStyleDefault 339 handler:nil]; 340 [alertController addAction:okAction]; 341 alertController.preferredAction = okAction; 342 343 [self.baseViewController presentViewController:alertController 344 animated:YES 345 completion:nil]; 346} 347 348#pragma mark - Private 349 350// Shows confirmation dialog before opening Other passwords. 351- (void)showConfirmationDialogToUseOtherPassword { 352 WebStateList* webStateList = self.browser->GetWebStateList(); 353 const GURL& URL = webStateList->GetActiveWebState()->GetLastCommittedURL(); 354 base::string16 origin = base::ASCIIToUTF16( 355 password_manager::GetShownOrigin(url::Origin::Create(URL))); 356 NSString* title = 357 l10n_util::GetNSString(IDS_IOS_CONFIRM_USING_OTHER_PASSWORD_TITLE); 358 NSString* message = l10n_util::GetNSStringF( 359 IDS_IOS_CONFIRM_USING_OTHER_PASSWORD_DESCRIPTION, origin); 360 361 self.alertCoordinator = [[AlertCoordinator alloc] 362 initWithBaseViewController:self.baseViewController 363 browser:self.browser 364 title:title 365 message:message]; 366 367 __weak __typeof__(self) weakSelf = self; 368 369 [self.alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL) 370 action:nil 371 style:UIAlertActionStyleCancel]; 372 373 NSString* actionTitle = 374 l10n_util::GetNSString(IDS_IOS_CONFIRM_USING_OTHER_PASSWORD_CONTINUE); 375 [self.alertCoordinator addItemWithTitle:actionTitle 376 action:^{ 377 [weakSelf showAllPasswords]; 378 } 379 style:UIAlertActionStyleDefault]; 380 381 [self.alertCoordinator start]; 382} 383 384// Opens other passwords. 385- (void)showAllPasswords { 386 [self reset]; 387 self.allPasswordCoordinator = [[ManualFillAllPasswordCoordinator alloc] 388 initWithBaseViewController:self.baseViewController 389 browser:self.browser 390 injectionHandler:self.injectionHandler]; 391 [self.allPasswordCoordinator start]; 392} 393 394@end 395