Commit 8062b71b authored by Scott Wu's avatar Scott Wu Committed by Commit Bot

Enable basic password autofill functionality for ios web view - Save password...

Enable basic password autofill functionality for ios web view - Save password after login and autofill password form when going back to the login page.

Bug: 865114
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I4f6e3f40500bb667959110d5f0eb3f07c85bca94
Reviewed-on: https://chromium-review.googlesource.com/1198672Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Commit-Queue: Scott Wu <scottwu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590146}
parent 36cac61c
...@@ -84,9 +84,11 @@ if (ios_web_view_enable_autofill) { ...@@ -84,9 +84,11 @@ if (ios_web_view_enable_autofill) {
"public/cwv_autofill_suggestion.h", "public/cwv_autofill_suggestion.h",
"public/cwv_credit_card.h", "public/cwv_credit_card.h",
"public/cwv_credit_card_verifier.h", "public/cwv_credit_card_verifier.h",
"public/cwv_password_controller.h",
"public/cwv_preferences_autofill.h", "public/cwv_preferences_autofill.h",
"public/cwv_web_view_autofill.h", "public/cwv_web_view_autofill.h",
"public/cwv_web_view_configuration_autofill.h", "public/cwv_web_view_configuration_autofill.h",
"public/cwv_web_view_password.h",
] ]
} }
...@@ -228,6 +230,8 @@ if (ios_web_view_enable_autofill) { ...@@ -228,6 +230,8 @@ if (ios_web_view_enable_autofill) {
"internal/autofill/cwv_credit_card_internal.h", "internal/autofill/cwv_credit_card_internal.h",
"internal/autofill/cwv_credit_card_verifier.mm", "internal/autofill/cwv_credit_card_verifier.mm",
"internal/autofill/cwv_credit_card_verifier_internal.h", "internal/autofill/cwv_credit_card_verifier_internal.h",
"internal/passwords/cwv_password_controller.mm",
"internal/passwords/cwv_password_controller_internal.h",
] ]
} }
...@@ -256,6 +260,7 @@ ios_web_view_deps = [ ...@@ -256,6 +260,7 @@ ios_web_view_deps = [
"//components/password_manager/core/browser", "//components/password_manager/core/browser",
"//components/password_manager/core/browser/form_parsing:form_parsing", "//components/password_manager/core/browser/form_parsing:form_parsing",
"//components/password_manager/core/common", "//components/password_manager/core/common",
"//components/password_manager/ios",
"//components/password_manager/sync/browser", "//components/password_manager/sync/browser",
"//components/pref_registry", "//components/pref_registry",
"//components/prefs", "//components/prefs",
......
...@@ -14,6 +14,7 @@ include_rules = [ ...@@ -14,6 +14,7 @@ include_rules = [
"+components/language/core/common", "+components/language/core/common",
"+components/net_log", "+components/net_log",
"+components/password_manager/core", "+components/password_manager/core",
"+components/password_manager/ios",
"+components/password_manager/sync", "+components/password_manager/sync",
"+components/pref_registry", "+components/pref_registry",
"+components/prefs", "+components/prefs",
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#import "ios/web_view/internal/cwv_script_command_internal.h" #import "ios/web_view/internal/cwv_script_command_internal.h"
#import "ios/web_view/internal/cwv_scroll_view_internal.h" #import "ios/web_view/internal/cwv_scroll_view_internal.h"
#import "ios/web_view/internal/cwv_web_view_configuration_internal.h" #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
#import "ios/web_view/internal/passwords/cwv_password_controller_internal.h"
#import "ios/web_view/internal/translate/cwv_translation_controller_internal.h" #import "ios/web_view/internal/translate/cwv_translation_controller_internal.h"
#import "ios/web_view/internal/translate/web_view_translate_client.h" #import "ios/web_view/internal/translate/web_view_translate_client.h"
#include "ios/web_view/internal/web_view_browser_state.h" #include "ios/web_view/internal/web_view_browser_state.h"
...@@ -103,6 +104,7 @@ NSDictionary* NSDictionaryFromDictionaryValue( ...@@ -103,6 +104,7 @@ NSDictionary* NSDictionaryFromDictionaryValue(
@property(nonatomic, readwrite) NSURL* visibleURL; @property(nonatomic, readwrite) NSURL* visibleURL;
#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
@property(nonatomic, readonly) CWVAutofillController* autofillController; @property(nonatomic, readonly) CWVAutofillController* autofillController;
@property(nonatomic, readonly) CWVPasswordController* passwordController;
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
// Updates the availability of the back/forward navigation properties exposed // Updates the availability of the back/forward navigation properties exposed
...@@ -115,6 +117,8 @@ NSDictionary* NSDictionaryFromDictionaryValue( ...@@ -115,6 +117,8 @@ NSDictionary* NSDictionaryFromDictionaryValue(
#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
// Returns a new CWVAutofillController created from |_webState|. // Returns a new CWVAutofillController created from |_webState|.
- (CWVAutofillController*)newAutofillController; - (CWVAutofillController*)newAutofillController;
// Returns a new CWVPasswordController created from |_webState|.
- (CWVPasswordController*)newPasswordController;
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
// Returns a new CWVTranslationController created from |_webState|. // Returns a new CWVTranslationController created from |_webState|.
- (CWVTranslationController*)newTranslationController; - (CWVTranslationController*)newTranslationController;
...@@ -129,6 +133,7 @@ static NSString* gUserAgentProduct = nil; ...@@ -129,6 +133,7 @@ static NSString* gUserAgentProduct = nil;
#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
@synthesize autofillController = _autofillController; @synthesize autofillController = _autofillController;
@synthesize passwordController = _passwordController;
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
@synthesize canGoBack = _canGoBack; @synthesize canGoBack = _canGoBack;
@synthesize canGoForward = _canGoForward; @synthesize canGoForward = _canGoForward;
...@@ -502,6 +507,20 @@ static NSString* gUserAgentProduct = nil; ...@@ -502,6 +507,20 @@ static NSString* gUserAgentProduct = nil;
JSAutofillManager:JSAutofillManager JSAutofillManager:JSAutofillManager
JSSuggestionManager:JSSuggestionManager]; JSSuggestionManager:JSSuggestionManager];
} }
#pragma mark - Password
- (CWVPasswordController*)passwordController {
if (!_passwordController) {
_passwordController = [self newPasswordController];
}
return _passwordController;
}
- (CWVPasswordController*)newPasswordController {
return [[CWVPasswordController alloc] initWithWebState:_webState.get()];
}
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
#pragma mark - Preserving and Restoring State #pragma mark - Preserving and Restoring State
...@@ -589,6 +608,7 @@ static NSString* gUserAgentProduct = nil; ...@@ -589,6 +608,7 @@ static NSString* gUserAgentProduct = nil;
#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
// Recreate and restore the delegate only if previously lazily loaded. // Recreate and restore the delegate only if previously lazily loaded.
// TODO(crbug.com/865114): Add delegate for password controller.
if (_autofillController) { if (_autofillController) {
id<CWVAutofillControllerDelegate> delegate = _autofillController.delegate; id<CWVAutofillControllerDelegate> delegate = _autofillController.delegate;
_autofillController = [self newAutofillController]; _autofillController = [self newAutofillController];
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web_view/internal/passwords/cwv_password_controller_internal.h"
#include <memory>
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/ios/browser/autofill_util.h"
#import "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
#include "components/password_manager/core/browser/password_manager.h"
#import "components/password_manager/ios/password_controller_helper.h"
#import "ios/web/public/origin_util.h"
#include "ios/web/public/url_scheme_util.h"
#import "ios/web/public/web_state/web_state_observer_bridge.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/web_view_browser_state.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using autofill::FormData;
using autofill::PasswordForm;
using ios_web_view::WebViewPasswordManagerClient;
using ios_web_view::WebViewPasswordManagerDriver;
using password_manager::GetPageURLAndCheckTrustLevel;
@interface CWVPasswordController ()<CRWWebStateObserver,
CWVPasswordManagerClientDelegate,
CWVPasswordManagerDriverDelegate,
PasswordControllerHelperDelegate>
// The PasswordManagerDriver owned by this PasswordController.
@property(nonatomic, readonly)
password_manager::PasswordManagerDriver* passwordManagerDriver;
// Helper contains common password controller logic.
@property(nonatomic, readonly) PasswordControllerHelper* helper;
// Informs the |_passwordManager| of the password forms (if any were present)
// that have been found on the page.
- (void)didFinishPasswordFormExtraction:
(const std::vector<autofill::PasswordForm>&)forms;
// Finds all password forms in DOM and sends them to the password manager for
// further processing.
- (void)findPasswordFormsAndSendThemToPasswordManager;
@end
@implementation CWVPasswordController {
std::unique_ptr<password_manager::PasswordManager> _passwordManager;
std::unique_ptr<WebViewPasswordManagerClient> _passwordManagerClient;
std::unique_ptr<WebViewPasswordManagerDriver> _passwordManagerDriver;
// The WebState this instance is observing. Will be null after
// -webStateDestroyed: has been called.
web::WebState* _webState;
// Bridge to observe WebState from Objective-C.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
// True indicates that credentials has been sent to the password manager.
BOOL _credentialsSentToPasswordManager;
// Bridge to observe form activity in |webState_|.
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
// TODO(crbug.com/865114): Add suggestion logic.
}
#pragma mark - Properties
@synthesize helper = _helper;
- (password_manager::PasswordManagerDriver*)passwordManagerDriver {
return _passwordManagerDriver.get();
}
#pragma mark - Initialization
- (instancetype)initWithWebState:(web::WebState*)webState {
self = [super init];
if (self) {
DCHECK(webState);
_webState = webState;
_webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get());
_helper = [[PasswordControllerHelper alloc] initWithWebState:webState
delegate:self];
_passwordManagerClient =
std::make_unique<WebViewPasswordManagerClient>(self);
_passwordManager = std::make_unique<password_manager::PasswordManager>(
_passwordManagerClient.get());
_passwordManagerDriver =
std::make_unique<WebViewPasswordManagerDriver>(self);
_credentialsSentToPasswordManager = NO;
// TODO(crbug.com/865114): Credential manager related logic
}
return self;
}
#pragma mark - Dealloc
- (void)dealloc {
if (_webState) {
_webState->RemoveObserver(_webStateObserverBridge.get());
}
}
#pragma mark - CRWWebStateObserver
- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
DCHECK_EQ(_webState, webState);
// Clear per-page state
// TODO(crbug.com/865114): Clear fillData and suggestion.
_credentialsSentToPasswordManager = NO;
// Retrieve the identity of the page. In case the page might be malicous,
// returns early.
GURL pageURL;
if (!GetPageURLAndCheckTrustLevel(webState, &pageURL)) {
return;
}
if (!web::UrlHasWebScheme(pageURL)) {
return;
}
// Notify the password manager that the page loaded so it can clear its own
// per-page state.
_passwordManager->DidNavigateMainFrame();
if (!webState->ContentIsHTML()) {
// If the current page is not HTML, it does not contain any HTML forms.
[self
didFinishPasswordFormExtraction:std::vector<autofill::PasswordForm>()];
}
[self findPasswordFormsAndSendThemToPasswordManager];
}
- (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(_webState, webState);
if (_webState) {
_webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset();
_webState = nullptr;
}
_passwordManagerDriver.reset();
_passwordManager.reset();
_passwordManagerClient.reset();
}
#pragma mark - CWVPasswordManagerClientDelegate
- (ios_web_view::WebViewBrowserState*)browserState {
return _webState ? ios_web_view::WebViewBrowserState::FromBrowserState(
_webState->GetBrowserState())
: nullptr;
}
- (password_manager::PasswordManager*)passwordManager {
return _passwordManager.get();
}
- (const GURL&)lastCommittedURL {
return self.helper.lastCommittedURL;
}
- (void)showSavePasswordInfoBar:
(std::unique_ptr<password_manager::PasswordFormManagerForUI>)formToSave {
// Always save password for Demo.
// TODO(crbug.com/865114): Implement remaining logic.
formToSave.get()->Save();
}
- (void)showUpdatePasswordInfoBar:
(std::unique_ptr<password_manager::PasswordFormManagerForUI>)formToUpdate {
// TODO(crbug.com/865114): Implement remaining logic.
}
- (void)showAutosigninNotification:
(std::unique_ptr<autofill::PasswordForm>)formSignedIn {
// TODO(crbug.com/865114): Implement remaining logic.
}
#pragma mark - CWVPasswordManagerDriverDelegate
- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData {
// TODO(crbug.com/865114): Add suggestion related logic.
[self.helper fillPasswordForm:formData completionHandler:nil];
}
// Informs delegate that there are no saved credentials for the current page.
- (void)informNoSavedCredentials {
// TODO(crbug.com/865114): Implement remaining logic.
}
#pragma mark - PasswordControllerHelperDelegate
- (void)helper:(PasswordControllerHelper*)helper
didSubmitForm:(const PasswordForm&)form
inMainFrame:(BOOL)inMainFrame {
if (inMainFrame) {
self.passwordManager->OnPasswordFormSubmitted(self.passwordManagerDriver,
form);
} else {
// Show a save prompt immediately because for iframes it is very hard to
// figure out correctness of password forms submission.
self.passwordManager->OnPasswordFormSubmittedNoChecks(
self.passwordManagerDriver, form);
}
}
#pragma mark - Private methods
- (void)didFinishPasswordFormExtraction:
(const std::vector<autofill::PasswordForm>&)forms {
// Do nothing if |self| has been detached.
if (!_passwordManager) {
return;
}
if (!forms.empty()) {
// TODO(crbug.com/865114):
// Notify web_state about password forms, so that this can be taken into
// account for the security state.
_credentialsSentToPasswordManager = YES;
// Invoke the password manager callback to autofill password forms
// on the loaded page.
_passwordManager->OnPasswordFormsParsed(self.passwordManagerDriver, forms);
} else {
[self informNoSavedCredentials];
}
// Invoke the password manager callback to check if password was
// accepted or rejected.
_passwordManager->OnPasswordFormsRendered(self.passwordManagerDriver, forms,
/*did_stop_loading=*/true);
}
- (void)findPasswordFormsAndSendThemToPasswordManager {
// Read all password forms from the page and send them to the password
// manager.
__weak CWVPasswordController* weakSelf = self;
[self.helper findPasswordFormsWithCompletionHandler:^(
const std::vector<autofill::PasswordForm>& forms) {
[weakSelf didFinishPasswordFormExtraction:forms];
}];
}
@end
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_CONTROLLER_INTERNAL_H_
#define IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_CONTROLLER_INTERNAL_H_
#import "ios/web/public/web_state/web_state.h"
#import "ios/web_view/public/cwv_password_controller.h"
NS_ASSUME_NONNULL_BEGIN
@interface CWVPasswordController ()
- (instancetype)initWithWebState:(web::WebState*)webState;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_CONTROLLER_INTERNAL_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_CONTROLLER_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_CONTROLLER_H_
#import <Foundation/Foundation.h>
#import "cwv_export.h"
NS_ASSUME_NONNULL_BEGIN
CWV_EXPORT
// Exposes features that allow saving entered passwords as well as autofilling
// password forms.
@interface CWVPasswordController : NSObject
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_CONTROLLER_H_
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#import "cwv_web_view.h" #import "cwv_web_view.h"
@class CWVAutofillController;
@interface CWVWebView (Autofill) @interface CWVWebView (Autofill)
// The web view's autofill controller. // The web view's autofill controller.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_PASSWORD_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_PASSWORD_H_
#import "cwv_web_view.h"
@class CWVPasswordController;
@interface CWVWebView (Password)
// The web view's password controller.
@property(nonatomic, readonly) CWVPasswordController* passwordController;
@end
#endif // IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_PASSWORD_H_
...@@ -270,6 +270,10 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier = ...@@ -270,6 +270,10 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
_autofillDelegate = [[ShellAutofillDelegate alloc] init]; _autofillDelegate = [[ShellAutofillDelegate alloc] init];
_webView.autofillController.delegate = _autofillDelegate; _webView.autofillController.delegate = _autofillDelegate;
// Triggers password controller initiation.
// TODO(crbug.com/865114): Add delegate for password controller.
[_webView passwordController];
[_webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | [_webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight]; UIViewAutoresizingFlexibleHeight];
[_containerView addSubview:_webView]; [_containerView addSubview:_webView];
......
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