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 @@
#include "components/autofill/core/browser/ui/popup_item_ids.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_bridge.h"
#import "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/browser/js_autofill_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/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
......@@ -34,9 +32,7 @@
#include "ios/web/public/js_messaging/web_frame_util.h"
#import "ios/web/public/js_messaging/web_frames_manager.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"
#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_profile_internal.h"
#import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
......@@ -52,7 +48,6 @@
#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/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"
#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"
......@@ -63,15 +58,6 @@
using autofill::FormRendererId;
using autofill::FieldRendererId;
@interface CWVAutofillController () <AutofillDriverIOSBridge,
CRWWebStateObserver,
CWVAutofillClientIOSBridge,
FormActivityObserver,
PasswordManagerClientBridge,
SharedPasswordControllerDelegate>
@end
@implementation CWVAutofillController {
// Bridge to observe the |webState|.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
......@@ -110,8 +96,9 @@ using autofill::FieldRendererId;
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
NSString* _lastFocusFormActivityWebFrameID;
NSString* _lastFormActivityWebFrameID;
NSString* _lastFormActivityTypedValue;
NSString* _lastFormActivityType;
FormRendererId _lastFormActivityUniqueFormID;
FieldRendererId _lastFormActivityUniqueFieldID;
}
......@@ -231,8 +218,8 @@ using autofill::FieldRendererId;
fieldIdentifier:fieldIdentifier
uniqueFieldID:_lastFormActivityUniqueFieldID
fieldType:fieldType
type:nil
typedValue:nil
type:_lastFormActivityType
typedValue:_lastFormActivityTypedValue
frameID:frameID];
// It is necessary to call |checkIfSuggestionsAvailableForForm| before
// |retrieveSuggestionsForForm| because the former actually queries the db,
......@@ -315,19 +302,19 @@ using autofill::FieldRendererId;
- (void)focusPreviousField {
[_JSSuggestionManager
selectPreviousElementInFrameWithID:_lastFocusFormActivityWebFrameID];
selectPreviousElementInFrameWithID:_lastFormActivityWebFrameID];
}
- (void)focusNextField {
[_JSSuggestionManager
selectNextElementInFrameWithID:_lastFocusFormActivityWebFrameID];
selectNextElementInFrameWithID:_lastFormActivityWebFrameID];
}
- (void)checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler:
(void (^)(BOOL previous, BOOL next))completionHandler {
[_JSSuggestionManager
fetchPreviousAndNextElementsPresenceInFrameWithID:
_lastFocusFormActivityWebFrameID
_lastFormActivityWebFrameID
completionHandler:completionHandler];
}
......@@ -449,7 +436,8 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)propagateAutofillPredictionsForForms:
(const std::vector<autofill::FormStructure*>&)forms {
// Not supported.
_passwordManager->ProcessAutofillPredictions(_passwordManagerDriver.get(),
forms);
}
#pragma mark - AutofillDriverIOSBridge
......@@ -498,9 +486,13 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
NSString* nsFieldType = base::SysUTF8ToNSString(params.field_type);
NSString* nsFrameID = base::SysUTF8ToNSString(GetWebFrameId(frame));
NSString* nsValue = base::SysUTF8ToNSString(params.value);
NSString* nsType = base::SysUTF8ToNSString(params.type);
BOOL userInitiated = params.has_user_gesture;
_lastFormActivityWebFrameID = nsFrameID;
_lastFormActivityTypedValue = nsValue;
_lastFormActivityType = nsType;
if (params.type == "focus") {
_lastFocusFormActivityWebFrameID = nsFrameID;
if ([_delegate respondsToSelector:@selector
(autofillController:
didFocusOnFieldWithIdentifier:fieldType:formName:frameID
......@@ -514,7 +506,6 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
userInitiated:userInitiated];
}
} else if (params.type == "input") {
_lastFocusFormActivityWebFrameID = nsFrameID;
if ([_delegate respondsToSelector:@selector
(autofillController:
didInputInFieldWithIdentifier:fieldType:formName:frameID
......@@ -688,7 +679,16 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)sharedPasswordController:(SharedPasswordController*)controller
showGeneratedPotentialPassword:(NSString*)generatedPotentialPassword
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
......
......@@ -8,6 +8,12 @@
#include <memory>
#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"
NS_ASSUME_NONNULL_BEGIN
......@@ -34,7 +40,12 @@ class WebState;
@class JsSuggestionManager;
@class SharedPasswordController;
@interface CWVAutofillController ()
@interface CWVAutofillController () <AutofillDriverIOSBridge,
CRWWebStateObserver,
CWVAutofillClientIOSBridge,
FormActivityObserver,
PasswordManagerClientBridge,
SharedPasswordControllerDelegate>
- (instancetype)
initWithWebState:(web::WebState*)webState
......
......@@ -440,6 +440,7 @@ TEST_F(CWVAutofillControllerTest, SubmitCallback) {
[delegate verify];
}
// Tests that CWVAutofillController notifies user of password leaks.
TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
......@@ -463,4 +464,28 @@ TEST_F(CWVAutofillControllerTest, NotifyUserOfLeak) {
[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
......@@ -20,7 +20,7 @@ WebViewPasswordFeatureManager::WebViewPasswordFeatureManager(
: pref_service_(pref_service), sync_service_(sync_service) {}
bool WebViewPasswordFeatureManager::IsGenerationEnabled() const {
return false;
return true;
}
bool WebViewPasswordFeatureManager::IsOptedInForAccountStorage() const {
......
......@@ -10,6 +10,8 @@
#include "base/macros.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_driver.h"
#include "components/password_manager/ios/password_manager_driver_bridge.h"
......@@ -29,6 +31,8 @@ class WebViewPasswordManagerDriver
const autofill::PasswordFormFillData& form_data) override;
void InformNoSavedCredentials(
bool should_show_popup_without_passwords) override;
void FormEligibleForGenerationFound(
const autofill::PasswordFormGenerationData& form) override;
void GeneratedPasswordAccepted(const base::string16& password) override;
void FillSuggestion(const base::string16& username,
const base::string16& password) override;
......
......@@ -39,6 +39,15 @@ void WebViewPasswordManagerDriver::InformNoSavedCredentials(
[bridge_ onNoSavedCredentials];
}
void WebViewPasswordManagerDriver::FormEligibleForGenerationFound(
const autofill::PasswordFormGenerationData& form) {
if (GetPasswordGenerationHelper() &&
GetPasswordGenerationHelper()->IsGenerationEnabled(
/*log_debug_data*/ true)) {
[bridge_ formEligibleForGenerationFound:form];
}
}
void WebViewPasswordManagerDriver::GeneratedPasswordAccepted(
const base::string16& password) {
NOTIMPLEMENTED();
......@@ -62,7 +71,7 @@ void WebViewPasswordManagerDriver::ClearPreviewedForm() {
password_manager::PasswordGenerationFrameHelper*
WebViewPasswordManagerDriver::GetPasswordGenerationHelper() {
return nullptr;
return [bridge_ passwordGenerationHelper];
}
PasswordManager* WebViewPasswordManagerDriver::GetPasswordManager() {
......
......@@ -155,6 +155,14 @@ typedef NS_OPTIONS(NSInteger, CWVPasswordLeakType) {
notifyUserOfPasswordLeakOnURL:(NSURL*)URL
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
NS_ASSUME_NONNULL_END
......
......@@ -283,20 +283,31 @@
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 ?: @""];
return [UIAlertAction actionWithTitle:title
__weak ShellAutofillDelegate* weakSelf = self;
return [UIAlertAction
actionWithTitle:title
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* _Nonnull action) {
[_autofillController
acceptSuggestion:suggestion
handler:^(UIAlertAction* action) {
ShellAutofillDelegate* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[strongSelf.autofillController acceptSuggestion:suggestion
completionHandler:nil];
[UIApplication.sharedApplication.keyWindow
endEditing:YES];
[UIApplication.sharedApplication.keyWindow endEditing:YES];
}];
}
......
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