Commit dea3009c authored by John Z Wu's avatar John Z Wu Committed by Commit Bot

Suggest password to CWVAutofillControllerDelegate

Allow client to display a "Suggest password..." suggestion to the user.
Upon tapping this suggestion, trigger delegate method asking the user
to accept the suggested password.

Bug: 1022906
Change-Id: I5a7264eb4efaab3c6bfcafe0a35d82f5eead6e53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2316847
Commit-Queue: John Wu <jzw@chromium.org>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799421}
parent 0f288d5b
...@@ -18,11 +18,9 @@ ...@@ -18,11 +18,9 @@
#include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/popup_item_ids.h"
#import "components/autofill/ios/browser/autofill_agent.h" #import "components/autofill/ios/browser/autofill_agent.h"
#include "components/autofill/ios/browser/autofill_driver_ios.h" #include "components/autofill/ios/browser/autofill_driver_ios.h"
#include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
#import "components/autofill/ios/browser/autofill_util.h" #import "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/browser/js_autofill_manager.h" #import "components/autofill/ios/browser/js_autofill_manager.h"
#import "components/autofill/ios/browser/js_suggestion_manager.h" #import "components/autofill/ios/browser/js_suggestion_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#include "components/autofill/ios/form_util/form_activity_params.h" #include "components/autofill/ios/form_util/form_activity_params.h"
#include "components/keyed_service/core/service_access_type.h" #include "components/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
...@@ -34,9 +32,7 @@ ...@@ -34,9 +32,7 @@
#include "ios/web/public/js_messaging/web_frame_util.h" #include "ios/web/public/js_messaging/web_frame_util.h"
#import "ios/web/public/js_messaging/web_frames_manager.h" #import "ios/web/public/js_messaging/web_frames_manager.h"
#import "ios/web/public/web_state.h" #import "ios/web/public/web_state.h"
#import "ios/web/public/web_state_observer_bridge.h"
#include "ios/web_view/internal/app/application_context.h" #include "ios/web_view/internal/app/application_context.h"
#import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h"
#import "ios/web_view/internal/autofill/cwv_autofill_form_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_form_internal.h"
#import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h"
#import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
...@@ -52,7 +48,6 @@ ...@@ -52,7 +48,6 @@
#include "ios/web_view/internal/autofill/web_view_strike_database_factory.h" #include "ios/web_view/internal/autofill/web_view_strike_database_factory.h"
#import "ios/web_view/internal/passwords/cwv_password_internal.h" #import "ios/web_view/internal/passwords/cwv_password_internal.h"
#import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h" #import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h"
#import "ios/web_view/internal/passwords/web_view_password_manager_client.h"
#import "ios/web_view/internal/passwords/web_view_password_manager_driver.h" #import "ios/web_view/internal/passwords/web_view_password_manager_driver.h"
#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h" #include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
#import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h" #import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
...@@ -63,15 +58,6 @@ ...@@ -63,15 +58,6 @@
using autofill::FormRendererId; using autofill::FormRendererId;
using autofill::FieldRendererId; using autofill::FieldRendererId;
@interface CWVAutofillController () <AutofillDriverIOSBridge,
CRWWebStateObserver,
CWVAutofillClientIOSBridge,
FormActivityObserver,
PasswordManagerClientBridge,
SharedPasswordControllerDelegate>
@end
@implementation CWVAutofillController { @implementation CWVAutofillController {
// Bridge to observe the |webState|. // Bridge to observe the |webState|.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge; std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
...@@ -110,8 +96,9 @@ using autofill::FieldRendererId; ...@@ -110,8 +96,9 @@ using autofill::FieldRendererId;
std::unique_ptr<autofill::FormActivityObserverBridge> std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge; _formActivityObserverBridge;
NSString* _lastFocusFormActivityWebFrameID; NSString* _lastFormActivityWebFrameID;
NSString* _lastFormActivityTypedValue;
NSString* _lastFormActivityType;
FormRendererId _lastFormActivityUniqueFormID; FormRendererId _lastFormActivityUniqueFormID;
FieldRendererId _lastFormActivityUniqueFieldID; FieldRendererId _lastFormActivityUniqueFieldID;
} }
...@@ -231,8 +218,8 @@ using autofill::FieldRendererId; ...@@ -231,8 +218,8 @@ using autofill::FieldRendererId;
fieldIdentifier:fieldIdentifier fieldIdentifier:fieldIdentifier
uniqueFieldID:_lastFormActivityUniqueFieldID uniqueFieldID:_lastFormActivityUniqueFieldID
fieldType:fieldType fieldType:fieldType
type:nil type:_lastFormActivityType
typedValue:nil typedValue:_lastFormActivityTypedValue
frameID:frameID]; frameID:frameID];
// It is necessary to call |checkIfSuggestionsAvailableForForm| before // It is necessary to call |checkIfSuggestionsAvailableForForm| before
// |retrieveSuggestionsForForm| because the former actually queries the db, // |retrieveSuggestionsForForm| because the former actually queries the db,
...@@ -315,19 +302,19 @@ using autofill::FieldRendererId; ...@@ -315,19 +302,19 @@ using autofill::FieldRendererId;
- (void)focusPreviousField { - (void)focusPreviousField {
[_JSSuggestionManager [_JSSuggestionManager
selectPreviousElementInFrameWithID:_lastFocusFormActivityWebFrameID]; selectPreviousElementInFrameWithID:_lastFormActivityWebFrameID];
} }
- (void)focusNextField { - (void)focusNextField {
[_JSSuggestionManager [_JSSuggestionManager
selectNextElementInFrameWithID:_lastFocusFormActivityWebFrameID]; selectNextElementInFrameWithID:_lastFormActivityWebFrameID];
} }
- (void)checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler: - (void)checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler:
(void (^)(BOOL previous, BOOL next))completionHandler { (void (^)(BOOL previous, BOOL next))completionHandler {
[_JSSuggestionManager [_JSSuggestionManager
fetchPreviousAndNextElementsPresenceInFrameWithID: fetchPreviousAndNextElementsPresenceInFrameWithID:
_lastFocusFormActivityWebFrameID _lastFormActivityWebFrameID
completionHandler:completionHandler]; completionHandler:completionHandler];
} }
...@@ -449,7 +436,8 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -449,7 +436,8 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)propagateAutofillPredictionsForForms: - (void)propagateAutofillPredictionsForForms:
(const std::vector<autofill::FormStructure*>&)forms { (const std::vector<autofill::FormStructure*>&)forms {
// Not supported. _passwordManager->ProcessAutofillPredictions(_passwordManagerDriver.get(),
forms);
} }
#pragma mark - AutofillDriverIOSBridge #pragma mark - AutofillDriverIOSBridge
...@@ -498,9 +486,13 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -498,9 +486,13 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
NSString* nsFieldType = base::SysUTF8ToNSString(params.field_type); NSString* nsFieldType = base::SysUTF8ToNSString(params.field_type);
NSString* nsFrameID = base::SysUTF8ToNSString(GetWebFrameId(frame)); NSString* nsFrameID = base::SysUTF8ToNSString(GetWebFrameId(frame));
NSString* nsValue = base::SysUTF8ToNSString(params.value); NSString* nsValue = base::SysUTF8ToNSString(params.value);
NSString* nsType = base::SysUTF8ToNSString(params.type);
BOOL userInitiated = params.has_user_gesture; BOOL userInitiated = params.has_user_gesture;
_lastFormActivityWebFrameID = nsFrameID;
_lastFormActivityTypedValue = nsValue;
_lastFormActivityType = nsType;
if (params.type == "focus") { if (params.type == "focus") {
_lastFocusFormActivityWebFrameID = nsFrameID;
if ([_delegate respondsToSelector:@selector if ([_delegate respondsToSelector:@selector
(autofillController: (autofillController:
didFocusOnFieldWithIdentifier:fieldType:formName:frameID didFocusOnFieldWithIdentifier:fieldType:formName:frameID
...@@ -514,7 +506,6 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -514,7 +506,6 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
userInitiated:userInitiated]; userInitiated:userInitiated];
} }
} else if (params.type == "input") { } else if (params.type == "input") {
_lastFocusFormActivityWebFrameID = nsFrameID;
if ([_delegate respondsToSelector:@selector if ([_delegate respondsToSelector:@selector
(autofillController: (autofillController:
didInputInFieldWithIdentifier:fieldType:formName:frameID didInputInFieldWithIdentifier:fieldType:formName:frameID
...@@ -688,7 +679,16 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -688,7 +679,16 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)sharedPasswordController:(SharedPasswordController*)controller - (void)sharedPasswordController:(SharedPasswordController*)controller
showGeneratedPotentialPassword:(NSString*)generatedPotentialPassword showGeneratedPotentialPassword:(NSString*)generatedPotentialPassword
decisionHandler:(void (^)(BOOL accept))decisionHandler { decisionHandler:(void (^)(BOOL accept))decisionHandler {
// No op. if ([self.delegate
respondsToSelector:@selector(autofillController:
suggestGeneratedPassword:decisionHandler:)]) {
[self.delegate autofillController:self
suggestGeneratedPassword:generatedPotentialPassword
decisionHandler:decisionHandler];
} else {
// If not implemented, just reject.
decisionHandler(/*accept=*/NO);
}
} }
- (void)sharedPasswordController:(SharedPasswordController*)controller - (void)sharedPasswordController:(SharedPasswordController*)controller
......
...@@ -8,6 +8,12 @@ ...@@ -8,6 +8,12 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "components/password_manager/ios/shared_password_controller.h"
#import "ios/web/public/web_state_observer_bridge.h"
#import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h"
#import "ios/web_view/internal/passwords/web_view_password_manager_client.h"
#import "ios/web_view/public/cwv_autofill_controller.h" #import "ios/web_view/public/cwv_autofill_controller.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
...@@ -34,7 +40,12 @@ class WebState; ...@@ -34,7 +40,12 @@ class WebState;
@class JsSuggestionManager; @class JsSuggestionManager;
@class SharedPasswordController; @class SharedPasswordController;
@interface CWVAutofillController () @interface CWVAutofillController () <AutofillDriverIOSBridge,
CRWWebStateObserver,
CWVAutofillClientIOSBridge,
FormActivityObserver,
PasswordManagerClientBridge,
SharedPasswordControllerDelegate>
- (instancetype) - (instancetype)
initWithWebState:(web::WebState*)webState initWithWebState:(web::WebState*)webState
......
...@@ -440,6 +440,7 @@ TEST_F(CWVAutofillControllerTest, SubmitCallback) { ...@@ -440,6 +440,7 @@ TEST_F(CWVAutofillControllerTest, SubmitCallback) {
[delegate verify]; [delegate verify];
} }
// Tests that CWVAutofillController notifies user of password leaks.
TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) { TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate)); id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate; autofill_controller_.delegate = delegate;
...@@ -463,4 +464,28 @@ TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) { ...@@ -463,4 +464,28 @@ TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) {
[delegate verify]; [delegate verify];
} }
// Tests that CWVAutofillController suggests passwords to its delegate.
TEST_F(CWVAutofillControllerTest, SuggestPasswordCallback) {
NSString* fake_generated_password = @"12345";
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
OCMExpect([delegate autofillController:autofill_controller_
suggestGeneratedPassword:fake_generated_password
decisionHandler:[OCMArg checkWithBlock:^(void (
^decisionHandler)(BOOL)) {
decisionHandler(/*accept=*/YES);
return YES;
}]]);
__block BOOL decision_handler_called = NO;
[autofill_controller_ sharedPasswordController:password_controller_
showGeneratedPotentialPassword:fake_generated_password
decisionHandler:^(BOOL accept) {
decision_handler_called = YES;
EXPECT_TRUE(accept);
}];
EXPECT_TRUE(decision_handler_called);
[delegate verify];
}
} // namespace ios_web_view } // namespace ios_web_view
...@@ -20,7 +20,7 @@ WebViewPasswordFeatureManager::WebViewPasswordFeatureManager( ...@@ -20,7 +20,7 @@ WebViewPasswordFeatureManager::WebViewPasswordFeatureManager(
: pref_service_(pref_service), sync_service_(sync_service) {} : pref_service_(pref_service), sync_service_(sync_service) {}
bool WebViewPasswordFeatureManager::IsGenerationEnabled() const { bool WebViewPasswordFeatureManager::IsGenerationEnabled() const {
return false; return true;
} }
bool WebViewPasswordFeatureManager::IsOptedInForAccountStorage() const { bool WebViewPasswordFeatureManager::IsOptedInForAccountStorage() const {
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/macros.h" #include "base/macros.h"
#include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
#include "components/password_manager/core/browser/password_manager.h" #include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_driver.h" #include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/ios/password_manager_driver_bridge.h" #include "components/password_manager/ios/password_manager_driver_bridge.h"
...@@ -29,6 +31,8 @@ class WebViewPasswordManagerDriver ...@@ -29,6 +31,8 @@ class WebViewPasswordManagerDriver
const autofill::PasswordFormFillData& form_data) override; const autofill::PasswordFormFillData& form_data) override;
void InformNoSavedCredentials( void InformNoSavedCredentials(
bool should_show_popup_without_passwords) override; bool should_show_popup_without_passwords) override;
void FormEligibleForGenerationFound(
const autofill::PasswordFormGenerationData& form) override;
void GeneratedPasswordAccepted(const base::string16& password) override; void GeneratedPasswordAccepted(const base::string16& password) override;
void FillSuggestion(const base::string16& username, void FillSuggestion(const base::string16& username,
const base::string16& password) override; const base::string16& password) override;
......
...@@ -39,6 +39,15 @@ void WebViewPasswordManagerDriver::InformNoSavedCredentials( ...@@ -39,6 +39,15 @@ void WebViewPasswordManagerDriver::InformNoSavedCredentials(
[bridge_ onNoSavedCredentials]; [bridge_ onNoSavedCredentials];
} }
void WebViewPasswordManagerDriver::FormEligibleForGenerationFound(
const autofill::PasswordFormGenerationData& form) {
if (GetPasswordGenerationHelper() &&
GetPasswordGenerationHelper()->IsGenerationEnabled(
/*log_debug_data*/ true)) {
[bridge_ formEligibleForGenerationFound:form];
}
}
void WebViewPasswordManagerDriver::GeneratedPasswordAccepted( void WebViewPasswordManagerDriver::GeneratedPasswordAccepted(
const base::string16& password) { const base::string16& password) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
...@@ -62,7 +71,7 @@ void WebViewPasswordManagerDriver::ClearPreviewedForm() { ...@@ -62,7 +71,7 @@ void WebViewPasswordManagerDriver::ClearPreviewedForm() {
password_manager::PasswordGenerationFrameHelper* password_manager::PasswordGenerationFrameHelper*
WebViewPasswordManagerDriver::GetPasswordGenerationHelper() { WebViewPasswordManagerDriver::GetPasswordGenerationHelper() {
return nullptr; return [bridge_ passwordGenerationHelper];
} }
PasswordManager* WebViewPasswordManagerDriver::GetPasswordManager() { PasswordManager* WebViewPasswordManagerDriver::GetPasswordManager() {
......
...@@ -155,6 +155,14 @@ typedef NS_OPTIONS(NSInteger, CWVPasswordLeakType) { ...@@ -155,6 +155,14 @@ typedef NS_OPTIONS(NSInteger, CWVPasswordLeakType) {
notifyUserOfPasswordLeakOnURL:(NSURL*)URL notifyUserOfPasswordLeakOnURL:(NSURL*)URL
leakType:(CWVPasswordLeakType)leakType; leakType:(CWVPasswordLeakType)leakType;
// Called when the user taps on the "Suggest password..." suggestion when trying
// to sign up for a new account on a site. |generatedPassword| is a randomly
// generated password that, if accepted in |decisionHandler|, will be injected
// into the form. |decisionHandler| must be called.
- (void)autofillController:(CWVAutofillController*)autofillController
suggestGeneratedPassword:(NSString*)generatedPassword
decisionHandler:(void (^)(BOOL accept))decisionHandler;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
......
...@@ -283,21 +283,32 @@ ...@@ -283,21 +283,32 @@
NSLog(@"Password on %@ is leaked!", URL); 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 #pragma mark - Private Methods
- (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion { - (UIAlertAction*)actionForSuggestion:(CWVAutofillSuggestion*)suggestion {
NSString* title = NSString* title =
[NSString stringWithFormat:@"%@ %@", suggestion.value, [NSString stringWithFormat:@"%@ %@", suggestion.value,
suggestion.displayDescription ?: @""]; suggestion.displayDescription ?: @""];
return [UIAlertAction actionWithTitle:title __weak ShellAutofillDelegate* weakSelf = self;
style:UIAlertActionStyleDefault return [UIAlertAction
handler:^(UIAlertAction* _Nonnull action) { actionWithTitle:title
[_autofillController style:UIAlertActionStyleDefault
acceptSuggestion:suggestion handler:^(UIAlertAction* action) {
completionHandler:nil]; ShellAutofillDelegate* strongSelf = weakSelf;
[UIApplication.sharedApplication.keyWindow if (!strongSelf) {
endEditing:YES]; return;
}]; }
[strongSelf.autofillController acceptSuggestion:suggestion
completionHandler:nil];
[UIApplication.sharedApplication.keyWindow endEditing:YES];
}];
} }
@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