shared_password_controller.mm 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  1. // Copyright 2018 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 "components/password_manager/ios/shared_password_controller.h"
  5. #include <stddef.h>
  6. #include <algorithm>
  7. #include <map>
  8. #include <memory>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/bind.h"
  13. #include "base/mac/foundation_util.h"
  14. #include "base/metrics/histogram_macros.h"
  15. #include "base/strings/sys_string_conversions.h"
  16. #include "base/strings/utf_string_conversions.h"
  17. #include "base/values.h"
  18. #include "components/autofill/core/browser/ui/popup_item_ids.h"
  19. #include "components/autofill/core/common/autofill_features.h"
  20. #include "components/autofill/core/common/form_data.h"
  21. #include "components/autofill/core/common/password_form_fill_data.h"
  22. #include "components/autofill/core/common/password_form_generation_data.h"
  23. #include "components/autofill/core/common/password_generation_util.h"
  24. #include "components/autofill/core/common/signatures.h"
  25. #include "components/autofill/core/common/unique_ids.h"
  26. #include "components/autofill/ios/browser/autofill_util.h"
  27. #import "components/autofill/ios/browser/form_suggestion_provider_query.h"
  28. #import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
  29. #include "components/autofill/ios/form_util/form_activity_params.h"
  30. #include "components/autofill/ios/form_util/unique_id_data_tab_helper.h"
  31. #include "components/password_manager/core/browser/password_bubble_experiment.h"
  32. #include "components/password_manager/core/browser/password_generation_frame_helper.h"
  33. #include "components/password_manager/core/browser/password_manager_client.h"
  34. #include "components/password_manager/core/browser/password_manager_driver.h"
  35. #include "components/password_manager/ios/account_select_fill_data.h"
  36. #include "components/password_manager/ios/password_manager_ios_util.h"
  37. #include "components/strings/grit/components_strings.h"
  38. #include "ios/web/common/url_scheme_util.h"
  39. #include "ios/web/public/js_messaging/web_frame.h"
  40. #include "ios/web/public/js_messaging/web_frame_util.h"
  41. #include "ios/web/public/navigation/navigation_context.h"
  42. #import "ios/web/public/web_state.h"
  43. #include "services/network/public/cpp/shared_url_loader_factory.h"
  44. #include "ui/base/l10n/l10n_util_mac.h"
  45. #include "url/gurl.h"
  46. #if !defined(__has_feature) || !__has_feature(objc_arc)
  47. #error "This file requires ARC support."
  48. #endif
  49. using autofill::FieldDataManager;
  50. using autofill::FieldRendererId;
  51. using autofill::FormActivityObserverBridge;
  52. using autofill::FormData;
  53. using autofill::FormRendererId;
  54. using autofill::PasswordFormGenerationData;
  55. using autofill::password_generation::LogPasswordGenerationEvent;
  56. using autofill::password_generation::PasswordGenerationType;
  57. using base::SysNSStringToUTF16;
  58. using base::SysNSStringToUTF8;
  59. using base::SysUTF16ToNSString;
  60. using base::SysUTF8ToNSString;
  61. using l10n_util::GetNSString;
  62. using l10n_util::GetNSStringF;
  63. using password_manager::AccountSelectFillData;
  64. using password_manager::FillData;
  65. using password_manager::GetPageURLAndCheckTrustLevel;
  66. using password_manager::JsonStringToFormData;
  67. using password_manager::PasswordFormManagerForUI;
  68. using password_manager::PasswordGenerationFrameHelper;
  69. using password_manager::PasswordManagerClient;
  70. using password_manager::PasswordManagerDriver;
  71. using password_manager::PasswordManagerInterface;
  72. using password_manager::metrics_util::LogPasswordDropdownShown;
  73. using password_manager::metrics_util::PasswordDropdownState;
  74. namespace {
  75. // Password is considered not generated when user edits it below 4 characters.
  76. constexpr int kMinimumLengthForEditedPassword = 4;
  77. // The string ' •••' appended to the username in the suggestion.
  78. NSString* const kSuggestionSuffix = @" ••••••••";
  79. } // namespace
  80. @interface SharedPasswordController ()
  81. // Helper contains common password suggestion logic.
  82. @property(nonatomic, readonly) PasswordSuggestionHelper* suggestionHelper;
  83. // Tracks if current password is generated.
  84. @property(nonatomic, assign) BOOL isPasswordGenerated;
  85. // Tracks field when current password was generated.
  86. @property(nonatomic) FieldRendererId passwordGeneratedIdentifier;
  87. // Tracks current potential generated password until accepted or rejected.
  88. @property(nonatomic, copy) NSString* generatedPotentialPassword;
  89. - (BOOL)isIncognito;
  90. @end
  91. @implementation SharedPasswordController {
  92. PasswordManagerInterface* _passwordManager;
  93. // The WebState this instance is observing. Will be null after
  94. // -webStateDestroyed: has been called.
  95. web::WebState* _webState;
  96. // Bridge to observe WebState from Objective-C.
  97. std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
  98. // Bridge to observe form activity in |_webState|.
  99. std::unique_ptr<FormActivityObserverBridge> _formActivityObserverBridge;
  100. // Form data for password generation on this page.
  101. std::map<FormRendererId, PasswordFormGenerationData> _formGenerationData;
  102. // Identifier of the field that was last typed into.
  103. FieldRendererId _lastTypedfieldIdentifier;
  104. // The value that was last typed by the user.
  105. NSString* _lastTypedValue;
  106. // Identifier of the last focused form.
  107. FormRendererId _lastFocusedFormIdentifier;
  108. // Identifier of the last focused field.
  109. FieldRendererId _lastFocusedFieldIdentifier;
  110. // Identifier of the last focused frame.
  111. web::WebFrame* _lastFocusedFrame;
  112. }
  113. - (instancetype)initWithWebState:(web::WebState*)webState
  114. manager:(password_manager::PasswordManagerInterface*)
  115. passwordManager
  116. formHelper:(PasswordFormHelper*)formHelper
  117. suggestionHelper:(PasswordSuggestionHelper*)suggestionHelper {
  118. self = [super init];
  119. if (self) {
  120. DCHECK(webState);
  121. _webState = webState;
  122. _webStateObserverBridge =
  123. std::make_unique<web::WebStateObserverBridge>(self);
  124. _webState->AddObserver(_webStateObserverBridge.get());
  125. _formActivityObserverBridge =
  126. std::make_unique<FormActivityObserverBridge>(_webState, self);
  127. _formHelper = formHelper;
  128. _formHelper.delegate = self;
  129. _suggestionHelper = suggestionHelper;
  130. _suggestionHelper.delegate = self;
  131. _passwordManager = passwordManager;
  132. }
  133. return self;
  134. }
  135. - (void)dealloc {
  136. if (_webState) {
  137. _webState->RemoveObserver(_webStateObserverBridge.get());
  138. }
  139. }
  140. - (BOOL)isIncognito {
  141. DCHECK(_delegate.passwordManagerClient);
  142. return _delegate.passwordManagerClient->IsIncognito();
  143. }
  144. #pragma mark - PasswordGenerationProvider
  145. - (void)triggerPasswordGeneration {
  146. if (!_lastFocusedFieldIdentifier) {
  147. return;
  148. }
  149. LogPasswordGenerationEvent(
  150. autofill::password_generation::PASSWORD_GENERATION_CONTEXT_MENU_PRESSED);
  151. [self generatePasswordForFormId:_lastFocusedFormIdentifier
  152. fieldIdentifier:_lastFocusedFieldIdentifier
  153. inFrame:_lastFocusedFrame
  154. isManuallyTriggered:YES];
  155. }
  156. #pragma mark - CRWWebStateObserver
  157. - (void)webState:(web::WebState*)webState
  158. didFinishNavigation:(web::NavigationContext*)navigation {
  159. DCHECK_EQ(_webState, webState);
  160. if (!navigation->HasCommitted() || navigation->IsSameDocument()) {
  161. return;
  162. }
  163. if (!GetPageURLAndCheckTrustLevel(webState, nullptr)) {
  164. return;
  165. }
  166. // Clear per-page state.
  167. [self.suggestionHelper resetForNewPage];
  168. auto fieldDataManager =
  169. UniqueIDDataTabHelper::FromWebState(_webState)->GetFieldDataManager();
  170. _passwordManager->PropagateFieldDataManagerInfo(
  171. *fieldDataManager, _delegate.passwordManagerDriver);
  172. // On non-iOS platforms navigations initiated by link click are excluded from
  173. // navigations which might be form submssions. On iOS there is no easy way to
  174. // check that the navigation is link initiated, so it is skipped. It should
  175. // not be so important since it is unlikely that the user clicks on a link
  176. // after filling password form w/o submitting it.
  177. _passwordManager->DidNavigateMainFrame(
  178. /*form_may_be_submitted=*/navigation->IsRendererInitiated());
  179. fieldDataManager->ClearData();
  180. }
  181. - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
  182. DCHECK_EQ(_webState, webState);
  183. // Retrieve the identity of the page. In case the page might be malicous,
  184. // returns early.
  185. GURL pageURL;
  186. if (!GetPageURLAndCheckTrustLevel(webState, &pageURL)) {
  187. return;
  188. }
  189. if (!web::UrlHasWebScheme(pageURL)) {
  190. return;
  191. }
  192. if (!webState->ContentIsHTML()) {
  193. // If the current page is not HTML, it does not contain any HTML forms.
  194. UniqueIDDataTabHelper* uniqueIDDataTabHelper =
  195. UniqueIDDataTabHelper::FromWebState(_webState);
  196. uint32_t maxUniqueID = uniqueIDDataTabHelper->GetNextAvailableRendererID();
  197. [self didFinishPasswordFormExtraction:std::vector<FormData>()
  198. withMaxUniqueID:maxUniqueID
  199. triggeredByFormChange:false];
  200. }
  201. }
  202. - (void)webState:(web::WebState*)webState
  203. frameDidBecomeAvailable:(web::WebFrame*)web_frame {
  204. DCHECK_EQ(_webState, webState);
  205. DCHECK(web_frame);
  206. if (!web_frame->CanCallJavaScriptFunction()) {
  207. return;
  208. }
  209. UniqueIDDataTabHelper* uniqueIDDataTabHelper =
  210. UniqueIDDataTabHelper::FromWebState(_webState);
  211. uint32_t nextAvailableRendererID =
  212. uniqueIDDataTabHelper->GetNextAvailableRendererID();
  213. [self.formHelper setUpForUniqueIDsWithInitialState:nextAvailableRendererID
  214. inFrame:web_frame];
  215. // Form parsing is run via the main frame for all same origin iframes.
  216. if (web_frame->IsMainFrame() && webState->ContentIsHTML()) {
  217. [self findPasswordFormsAndSendToPasswordStoreForFormChange:false];
  218. }
  219. }
  220. // Track detaching iframes.
  221. - (void)webState:(web::WebState*)webState
  222. frameWillBecomeUnavailable:(web::WebFrame*)web_frame {
  223. // No need to try to detect submissions when the webState is being destroyed.
  224. if (webState->IsBeingDestroyed()) {
  225. return;
  226. }
  227. if (web_frame->IsMainFrame() || !web_frame->CanCallJavaScriptFunction()) {
  228. return;
  229. }
  230. _passwordManager->OnIframeDetach(web_frame->GetFrameId(),
  231. _delegate.passwordManagerDriver,
  232. *self.formHelper.fieldDataManager);
  233. }
  234. - (void)webStateDestroyed:(web::WebState*)webState {
  235. DCHECK_EQ(_webState, webState);
  236. if (_webState) {
  237. _webState->RemoveObserver(_webStateObserverBridge.get());
  238. _webStateObserverBridge.reset();
  239. _formActivityObserverBridge.reset();
  240. _webState = nullptr;
  241. }
  242. _formGenerationData.clear();
  243. _isPasswordGenerated = NO;
  244. _lastTypedfieldIdentifier = FieldRendererId();
  245. _lastTypedValue = nil;
  246. _lastFocusedFormIdentifier = FormRendererId();
  247. _lastFocusedFieldIdentifier = FieldRendererId();
  248. _lastFocusedFrame = nullptr;
  249. _passwordManager = nullptr;
  250. }
  251. #pragma mark - FormSuggestionProvider
  252. - (void)checkIfSuggestionsAvailableForForm:
  253. (FormSuggestionProviderQuery*)formQuery
  254. isMainFrame:(BOOL)isMainFrame
  255. hasUserGesture:(BOOL)hasUserGesture
  256. webState:(web::WebState*)webState
  257. completionHandler:
  258. (SuggestionsAvailableCompletion)completion {
  259. if (!GetPageURLAndCheckTrustLevel(webState, nullptr)) {
  260. return;
  261. }
  262. [self.suggestionHelper
  263. checkIfSuggestionsAvailableForForm:formQuery
  264. isMainFrame:isMainFrame
  265. webState:webState
  266. completionHandler:^(BOOL suggestionsAvailable) {
  267. // Always display "Show All..." for password fields.
  268. completion([formQuery isOnPasswordField] ||
  269. suggestionsAvailable);
  270. }];
  271. if (self.isPasswordGenerated &&
  272. ([formQuery.type isEqual:@"input"] ||
  273. [formQuery.type isEqual:@"keyup"]) &&
  274. formQuery.uniqueFieldID == self.passwordGeneratedIdentifier) {
  275. // On other platforms, when the user clicks on generation field, we show
  276. // password in clear text. And the user has the possibility to edit it. On
  277. // iOS, it's harder to do (it's probably bad idea to change field type from
  278. // password to text). The decision was to give everything to the automatic
  279. // flow and avoid the manual flow, for a cleaner and simpler UI.
  280. if (formQuery.typedValue.length < kMinimumLengthForEditedPassword) {
  281. self.isPasswordGenerated = NO;
  282. LogPasswordGenerationEvent(
  283. autofill::password_generation::PASSWORD_DELETED);
  284. self.passwordGeneratedIdentifier = FieldRendererId();
  285. _passwordManager->OnPasswordNoLongerGenerated(
  286. _delegate.passwordManagerDriver);
  287. } else {
  288. web::WebFrame* frame = web::GetWebFrameWithId(
  289. _webState, SysNSStringToUTF8(formQuery.frameID));
  290. // Inject updated value to possibly update confirmation field.
  291. [self injectGeneratedPasswordForFormId:formQuery.uniqueFormID
  292. inFrame:frame
  293. generatedPassword:formQuery.typedValue
  294. completionHandler:nil];
  295. }
  296. }
  297. if (formQuery.uniqueFieldID != _lastTypedfieldIdentifier ||
  298. ![formQuery.typedValue isEqual:_lastTypedValue]) {
  299. // This method is called multiple times for the same user keystroke. Inform
  300. // only once the keystroke.
  301. _lastTypedfieldIdentifier = formQuery.uniqueFieldID;
  302. _lastTypedValue = formQuery.typedValue;
  303. if ([formQuery.type isEqual:@"input"] ||
  304. [formQuery.type isEqual:@"keyup"]) {
  305. [self.formHelper updateFieldDataOnUserInput:formQuery.uniqueFieldID
  306. inputValue:formQuery.typedValue];
  307. _passwordManager->UpdateStateOnUserInput(
  308. _delegate.passwordManagerDriver, formQuery.uniqueFormID,
  309. formQuery.uniqueFieldID, SysNSStringToUTF16(formQuery.typedValue));
  310. }
  311. }
  312. }
  313. - (void)retrieveSuggestionsForForm:(FormSuggestionProviderQuery*)formQuery
  314. webState:(web::WebState*)webState
  315. completionHandler:(SuggestionsReadyCompletion)completion {
  316. if (!GetPageURLAndCheckTrustLevel(webState, nullptr)) {
  317. return;
  318. }
  319. NSArray<FormSuggestion*>* rawSuggestions = [self.suggestionHelper
  320. retrieveSuggestionsWithFormID:formQuery.uniqueFormID
  321. fieldIdentifier:formQuery.uniqueFieldID
  322. fieldType:formQuery.fieldType];
  323. NSMutableArray<FormSuggestion*>* suggestions = [NSMutableArray array];
  324. bool isPasswordField = [formQuery isOnPasswordField];
  325. for (FormSuggestion* rawSuggestion in rawSuggestions) {
  326. // 1) If this is a focus event or the field is empty show all suggestions.
  327. // Otherwise:
  328. // 2) If this is a username field then show only credentials with matching
  329. // prefixes.
  330. // 3) If this is a password field then show suggestions only if
  331. // the field is empty.
  332. if (![formQuery hasFocusType] && formQuery.typedValue.length > 0 &&
  333. (isPasswordField ||
  334. ![rawSuggestion.value hasPrefix:formQuery.typedValue])) {
  335. continue;
  336. }
  337. DCHECK(self.delegate.passwordManagerClient);
  338. NSString* value =
  339. [rawSuggestion.value stringByAppendingString:kSuggestionSuffix];
  340. FormSuggestion* suggestion =
  341. [FormSuggestion suggestionWithValue:value
  342. displayDescription:rawSuggestion.displayDescription
  343. icon:nil
  344. identifier:0
  345. requiresReauth:YES];
  346. [suggestions addObject:suggestion];
  347. }
  348. absl::optional<PasswordDropdownState> suggestionState;
  349. if (suggestions.count) {
  350. suggestionState = PasswordDropdownState::kStandard;
  351. }
  352. if ([self canGeneratePasswordForForm:formQuery.uniqueFormID
  353. fieldIdentifier:formQuery.uniqueFieldID
  354. fieldType:formQuery.fieldType]) {
  355. // Add "Suggest Password...".
  356. NSString* suggestPassword = GetNSString(IDS_IOS_SUGGEST_PASSWORD);
  357. FormSuggestion* suggestion = [FormSuggestion
  358. suggestionWithValue:suggestPassword
  359. displayDescription:nil
  360. icon:nil
  361. identifier:autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY
  362. requiresReauth:NO];
  363. [suggestions addObject:suggestion];
  364. suggestionState = PasswordDropdownState::kStandardGenerate;
  365. }
  366. if (suggestionState) {
  367. LogPasswordDropdownShown(*suggestionState, [self isIncognito]);
  368. }
  369. completion([suggestions copy], self);
  370. }
  371. - (void)didSelectSuggestion:(FormSuggestion*)suggestion
  372. form:(NSString*)formName
  373. uniqueFormID:(FormRendererId)uniqueFormID
  374. fieldIdentifier:(NSString*)fieldIdentifier
  375. uniqueFieldID:(FieldRendererId)uniqueFieldID
  376. frameID:(NSString*)frameID
  377. completionHandler:(SuggestionHandledCompletion)completion {
  378. web::WebFrame* frame =
  379. web::GetWebFrameWithId(_webState, SysNSStringToUTF8(frameID));
  380. switch (suggestion.identifier) {
  381. case autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY: {
  382. completion();
  383. password_manager::metrics_util::LogPasswordDropdownItemSelected(
  384. password_manager::metrics_util::PasswordDropdownSelectedOption::
  385. kShowAll,
  386. [self isIncognito]);
  387. return;
  388. }
  389. case autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY: {
  390. // Don't call completion because current suggestion state should remain
  391. // whether user injects a generated password or cancels.
  392. [self generatePasswordForFormId:uniqueFormID
  393. fieldIdentifier:uniqueFieldID
  394. inFrame:frame
  395. isManuallyTriggered:NO];
  396. password_manager::metrics_util::LogPasswordDropdownItemSelected(
  397. password_manager::metrics_util::PasswordDropdownSelectedOption::
  398. kGenerate,
  399. [self isIncognito]);
  400. return;
  401. }
  402. default: {
  403. password_manager::metrics_util::LogPasswordDropdownItemSelected(
  404. password_manager::metrics_util::PasswordDropdownSelectedOption::
  405. kPassword,
  406. [self isIncognito]);
  407. DCHECK([suggestion.value hasSuffix:kSuggestionSuffix]);
  408. NSString* username = [suggestion.value
  409. substringToIndex:suggestion.value.length - kSuggestionSuffix.length];
  410. std::unique_ptr<password_manager::FillData> fillData =
  411. [self.suggestionHelper passwordFillDataForUsername:username];
  412. if (!fillData) {
  413. completion();
  414. return;
  415. }
  416. [self.formHelper fillPasswordFormWithFillData:*fillData
  417. inFrame:frame
  418. triggeredOnField:uniqueFieldID
  419. completionHandler:^(BOOL success) {
  420. completion();
  421. }];
  422. break;
  423. }
  424. }
  425. [_delegate sharedPasswordController:self didAcceptSuggestion:suggestion];
  426. }
  427. - (SuggestionProviderType)type {
  428. return SuggestionProviderTypePassword;
  429. }
  430. #pragma mark - PasswordManagerDriverDelegate
  431. - (const GURL&)lastCommittedURL {
  432. return _webState ? _webState->GetLastCommittedURL() : GURL::EmptyGURL();
  433. }
  434. - (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
  435. completionHandler:(void (^)(BOOL))completionHandler {
  436. [self.suggestionHelper processWithPasswordFormFillData:formData];
  437. [self.formHelper fillPasswordForm:formData
  438. inFrame:web::GetMainFrame(_webState)
  439. completionHandler:completionHandler];
  440. }
  441. - (void)onNoSavedCredentials {
  442. [self.suggestionHelper processWithNoSavedCredentials];
  443. }
  444. - (PasswordGenerationFrameHelper*)passwordGenerationHelper {
  445. return _delegate.passwordManagerDriver->GetPasswordGenerationHelper();
  446. }
  447. - (void)formEligibleForGenerationFound:(const PasswordFormGenerationData&)form {
  448. _formGenerationData[form.form_renderer_id] = form;
  449. }
  450. #pragma mark - PasswordFormHelperDelegate
  451. - (void)formHelper:(PasswordFormHelper*)formHelper
  452. didSubmitForm:(const FormData&)form
  453. inMainFrame:(BOOL)inMainFrame {
  454. if (inMainFrame) {
  455. _passwordManager->OnPasswordFormSubmitted(_delegate.passwordManagerDriver,
  456. form);
  457. } else {
  458. // Show a save prompt immediately because for iframes it is very hard to
  459. // figure out correctness of password forms submission.
  460. _passwordManager->OnSubframeFormSubmission(_delegate.passwordManagerDriver,
  461. form);
  462. }
  463. }
  464. #pragma mark - PasswordSuggestionHelperDelegate
  465. - (void)suggestionHelperShouldTriggerFormExtraction:
  466. (PasswordSuggestionHelper*)suggestionHelper {
  467. [self findPasswordFormsAndSendToPasswordStoreForFormChange:false];
  468. }
  469. #pragma mark - Private methods
  470. - (void)didFinishPasswordFormExtraction:(const std::vector<FormData>&)forms
  471. withMaxUniqueID:(uint32_t)maxID
  472. triggeredByFormChange:(BOOL)triggeredByFormChange {
  473. // Do nothing if |self| has been detached.
  474. if (!_passwordManager) {
  475. return;
  476. }
  477. if (!forms.empty()) {
  478. [self.suggestionHelper updateStateOnPasswordFormExtracted];
  479. UniqueIDDataTabHelper* uniqueIDDataTabHelper =
  480. UniqueIDDataTabHelper::FromWebState(_webState);
  481. // Update NextAvailableRendererId if a bigger value was extracted.
  482. if (uniqueIDDataTabHelper->GetNextAvailableRendererID() < maxID)
  483. uniqueIDDataTabHelper->SetNextAvailableRendererID(++maxID);
  484. // Invoke the password manager callback to autofill password forms
  485. // on the loaded page.
  486. _passwordManager->OnPasswordFormsParsed(_delegate.passwordManagerDriver,
  487. forms);
  488. } else {
  489. [self onNoSavedCredentials];
  490. }
  491. // Invoke the password manager callback to check if password was
  492. // accepted or rejected. If accepted, infobar is presented. If
  493. // rejected, the provisionally saved password is deleted. On Chrome
  494. // w/ a renderer, it is the renderer who calls OnPasswordFormsParsed()
  495. // and OnPasswordFormsRendered(). Bling has to improvised a bit on the
  496. // ordering of these two calls.
  497. // Only check for form submissions if forms are not being parsed due to
  498. // added elements to the form.
  499. if (!triggeredByFormChange) {
  500. _passwordManager->OnPasswordFormsRendered(_delegate.passwordManagerDriver,
  501. forms);
  502. }
  503. }
  504. - (void)findPasswordFormsAndSendToPasswordStoreForFormChange:
  505. (BOOL)triggeredByFormChange {
  506. // Read all password forms from the page and send them to the password
  507. // manager.
  508. __weak SharedPasswordController* weakSelf = self;
  509. [self.formHelper findPasswordFormsWithCompletionHandler:^(
  510. const std::vector<FormData>& forms, uint32_t maxID) {
  511. [weakSelf didFinishPasswordFormExtraction:forms
  512. withMaxUniqueID:maxID
  513. triggeredByFormChange:triggeredByFormChange];
  514. }];
  515. }
  516. - (BOOL)canGeneratePasswordForForm:(FormRendererId)formIdentifier
  517. fieldIdentifier:(FieldRendererId)fieldIdentifier
  518. fieldType:(NSString*)fieldType {
  519. if (!self.passwordGenerationHelper->IsGenerationEnabled(
  520. /*log_debug_data*/ true)) {
  521. return NO;
  522. }
  523. if (![fieldType isEqual:kPasswordFieldType]) {
  524. return NO;
  525. }
  526. const PasswordFormGenerationData* generationData =
  527. [self formForGenerationFromFormID:formIdentifier];
  528. if (!generationData) {
  529. return NO;
  530. }
  531. FieldRendererId newPasswordIdentifier =
  532. generationData->new_password_renderer_id;
  533. if (fieldIdentifier == newPasswordIdentifier) {
  534. return YES;
  535. }
  536. // Don't show password generation if the field is 'confirm password'.
  537. return NO;
  538. }
  539. - (const PasswordFormGenerationData*)formForGenerationFromFormID:
  540. (FormRendererId)formIdentifier {
  541. if (_formGenerationData.find(formIdentifier) != _formGenerationData.end()) {
  542. return &_formGenerationData[formIdentifier];
  543. }
  544. return nullptr;
  545. }
  546. - (void)generatePasswordForFormId:(FormRendererId)formIdentifier
  547. fieldIdentifier:(FieldRendererId)fieldIdentifier
  548. inFrame:(web::WebFrame*)frame
  549. isManuallyTriggered:(BOOL)isManuallyTriggered {
  550. const autofill::PasswordFormGenerationData* generationData =
  551. [self formForGenerationFromFormID:formIdentifier];
  552. if (!isManuallyTriggered && !generationData) {
  553. return;
  554. }
  555. BOOL shouldUpdateGenerationData =
  556. !generationData ||
  557. generationData->new_password_renderer_id != fieldIdentifier;
  558. if (isManuallyTriggered && shouldUpdateGenerationData) {
  559. PasswordFormGenerationData generationData = {
  560. .form_renderer_id = formIdentifier,
  561. .new_password_renderer_id = fieldIdentifier,
  562. };
  563. [self formEligibleForGenerationFound:generationData];
  564. }
  565. __weak SharedPasswordController* weakSelf = self;
  566. auto formDataCompletion = ^(BOOL found, const autofill::FormData& form) {
  567. autofill::FormSignature formSignature =
  568. found ? CalculateFormSignature(form) : autofill::FormSignature(0);
  569. autofill::FieldSignature fieldSignature = autofill::FieldSignature(0);
  570. int maxLength = 0;
  571. if (found) {
  572. for (const autofill::FormFieldData& field : form.fields) {
  573. if (field.unique_renderer_id == fieldIdentifier) {
  574. fieldSignature = CalculateFieldSignatureForField(field);
  575. maxLength = field.max_length;
  576. break;
  577. }
  578. }
  579. }
  580. std::u16string generatedPassword =
  581. weakSelf.passwordGenerationHelper->GeneratePassword(
  582. [weakSelf lastCommittedURL], formSignature, fieldSignature,
  583. maxLength);
  584. weakSelf.generatedPotentialPassword = SysUTF16ToNSString(generatedPassword);
  585. auto clearPotentialPassword = ^{
  586. weakSelf.generatedPotentialPassword = nil;
  587. };
  588. [weakSelf.delegate
  589. sharedPasswordController:weakSelf
  590. showGeneratedPotentialPassword:weakSelf.generatedPotentialPassword
  591. decisionHandler:^(BOOL accept) {
  592. if (accept) {
  593. LogPasswordGenerationEvent(
  594. autofill::password_generation::
  595. PASSWORD_ACCEPTED);
  596. [weakSelf
  597. injectGeneratedPasswordForFormId:formIdentifier
  598. inFrame:frame
  599. generatedPassword:
  600. weakSelf
  601. .generatedPotentialPassword
  602. completionHandler:
  603. clearPotentialPassword];
  604. } else {
  605. clearPotentialPassword();
  606. }
  607. }];
  608. };
  609. [self.formHelper extractPasswordFormData:formIdentifier
  610. completionHandler:formDataCompletion];
  611. _passwordManager->SetGenerationElementAndTypeForForm(
  612. _delegate.passwordManagerDriver, formIdentifier, fieldIdentifier,
  613. isManuallyTriggered ? PasswordGenerationType::kManual
  614. : PasswordGenerationType::kAutomatic);
  615. }
  616. - (void)injectGeneratedPasswordForFormId:(FormRendererId)formIdentifier
  617. inFrame:(web::WebFrame*)frame
  618. generatedPassword:(NSString*)generatedPassword
  619. completionHandler:(void (^)())completionHandler {
  620. const autofill::PasswordFormGenerationData* generationData =
  621. [self formForGenerationFromFormID:formIdentifier];
  622. if (!generationData) {
  623. return;
  624. }
  625. FieldRendererId newPasswordUniqueId =
  626. generationData->new_password_renderer_id;
  627. FieldRendererId confirmPasswordUniqueId =
  628. generationData->confirmation_password_renderer_id;
  629. __weak SharedPasswordController* weakSelf = self;
  630. auto generatedPasswordInjected = ^(BOOL success) {
  631. if (success) {
  632. [weakSelf onFilledPasswordForm:formIdentifier
  633. withGeneratedPassword:generatedPassword
  634. passwordUniqueId:newPasswordUniqueId];
  635. }
  636. if (completionHandler) {
  637. completionHandler();
  638. }
  639. };
  640. [self.formHelper fillPasswordForm:formIdentifier
  641. inFrame:frame
  642. newPasswordIdentifier:newPasswordUniqueId
  643. confirmPasswordIdentifier:confirmPasswordUniqueId
  644. generatedPassword:generatedPassword
  645. completionHandler:generatedPasswordInjected];
  646. }
  647. - (void)onFilledPasswordForm:(FormRendererId)formIdentifier
  648. withGeneratedPassword:(NSString*)generatedPassword
  649. passwordUniqueId:(FieldRendererId)newPasswordUniqueId {
  650. __weak SharedPasswordController* weakSelf = self;
  651. auto passwordPresaved = ^(BOOL found, const autofill::FormData& form) {
  652. // If the form isn't found, it disappeared between the call to
  653. // [self.formHelper fillPasswordForm:newPasswordIdentifier:...]
  654. // and here. There isn't much that can be done.
  655. if (!found)
  656. return;
  657. [weakSelf presaveGeneratedPassword:generatedPassword
  658. passwordUniqueId:newPasswordUniqueId
  659. formData:form];
  660. };
  661. [self.formHelper extractPasswordFormData:formIdentifier
  662. completionHandler:passwordPresaved];
  663. self.isPasswordGenerated = YES;
  664. self.passwordGeneratedIdentifier = newPasswordUniqueId;
  665. }
  666. - (void)presaveGeneratedPassword:(NSString*)generatedPassword
  667. passwordUniqueId:(FieldRendererId)newPasswordUniqueId
  668. formData:(const autofill::FormData&)formData {
  669. if (!_passwordManager)
  670. return;
  671. _passwordManager->PresaveGeneratedPassword(
  672. _delegate.passwordManagerDriver, formData,
  673. SysNSStringToUTF16(generatedPassword), newPasswordUniqueId);
  674. }
  675. // Checks that all fields with |fieldIds| have user input recorded by
  676. // the FieldDataManager.
  677. - (BOOL)allFieldsContainUserInput:
  678. (const std::vector<FieldRendererId>&)fieldIds {
  679. for (auto fieldId : fieldIds) {
  680. if (!self.formHelper.fieldDataManager->HasFieldData(fieldId)) {
  681. return NO;
  682. }
  683. }
  684. return YES;
  685. }
  686. #pragma mark - FormActivityObserver
  687. - (void)webState:(web::WebState*)webState
  688. didRegisterFormActivity:(const autofill::FormActivityParams&)params
  689. inFrame:(web::WebFrame*)frame {
  690. DCHECK_EQ(_webState, webState);
  691. GURL pageURL;
  692. if (!GetPageURLAndCheckTrustLevel(webState, &pageURL) || !frame ||
  693. !frame->CanCallJavaScriptFunction() || params.input_missing) {
  694. _lastFocusedFormIdentifier = FormRendererId();
  695. _lastFocusedFieldIdentifier = FieldRendererId();
  696. _lastFocusedFrame = nullptr;
  697. return;
  698. }
  699. if (params.type == "focus") {
  700. _lastFocusedFormIdentifier = params.unique_form_id;
  701. _lastFocusedFieldIdentifier = params.unique_field_id;
  702. _lastFocusedFrame = frame;
  703. }
  704. // If there's a change in password forms on a page, they should be parsed
  705. // again.
  706. if (params.type == "form_changed") {
  707. [self findPasswordFormsAndSendToPasswordStoreForFormChange:true];
  708. }
  709. // If the form was cleared PasswordManager should be informed to decide
  710. // whether it's a change password form that was submitted.
  711. if (params.type == "password_form_cleared") {
  712. FormData formData;
  713. if (!JsonStringToFormData(SysUTF8ToNSString(params.value), &formData,
  714. pageURL)) {
  715. return;
  716. }
  717. _passwordManager->OnPasswordFormCleared(_delegate.passwordManagerDriver,
  718. formData);
  719. }
  720. }
  721. // If the form was removed, PasswordManager should be informed to decide
  722. // whether the form was submitted.
  723. - (void)webState:(web::WebState*)webState
  724. didRegisterFormRemoval:(const autofill::FormRemovalParams&)params
  725. inFrame:(web::WebFrame*)frame {
  726. if (!params.unique_form_id) {
  727. // If formless password fields were removed, check that all of them had
  728. // user input.
  729. if (![self allFieldsContainUserInput:params.removed_unowned_fields]) {
  730. return;
  731. }
  732. }
  733. _passwordManager->OnPasswordFormRemoved(_delegate.passwordManagerDriver,
  734. *self.formHelper.fieldDataManager,
  735. params.unique_form_id);
  736. }
  737. @end