Commit 6b0b7526 authored by Olivier Robin's avatar Olivier Robin Committed by Commit Bot

Migrate Form activity observer to autofill::FormActivityObserver

Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Ib146d46a7b50ed06ee26434fdcbd7166672141fa
Reviewed-on: https://chromium-review.googlesource.com/1118549Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#574189}
parent 4feb130a
......@@ -35,6 +35,7 @@ source_set("browser") {
"//base",
"//components/autofill/core/browser",
"//components/autofill/core/common",
"//components/autofill/ios/form_util",
"//components/prefs:prefs",
"//components/prefs/ios",
"//google_apis",
......
......@@ -35,6 +35,7 @@
#include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/js_autofill_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
......@@ -89,7 +90,9 @@ void GetFormAndField(autofill::FormData* form,
} // namespace
@interface AutofillAgent ()<CRWWebStateObserver, PrefObserverDelegate>
@interface AutofillAgent ()<CRWWebStateObserver,
FormActivityObserver,
PrefObserverDelegate>
// Notifies the autofill manager when forms are detected on a page.
- (void)notifyAutofillManager:(autofill::AutofillManager*)autofillManager
......@@ -191,6 +194,10 @@ void GetFormAndField(autofill::FormData* form,
std::unique_ptr<PrefObserverBridge> prefObserverBridge_;
// Registrar for pref changes notifications.
PrefChangeRegistrar prefChangeRegistrar_;
// Bridge to observe form activity in |webState_|.
std::unique_ptr<autofill::FormActivityObserverBridge>
formActivityObserverBridge_;
}
- (instancetype)initWithPrefService:(PrefService*)prefService
......@@ -204,6 +211,8 @@ void GetFormAndField(autofill::FormData* form,
webStateObserverBridge_ =
std::make_unique<web::WebStateObserverBridge>(self);
webState_->AddObserver(webStateObserverBridge_.get());
formActivityObserverBridge_ =
std::make_unique<autofill::FormActivityObserverBridge>(webState_, self);
prefObserverBridge_ = std::make_unique<PrefObserverBridge>(self);
prefChangeRegistrar_.Init(prefService);
prefObserverBridge_->ObserveChangesForPreference(
......@@ -222,6 +231,7 @@ void GetFormAndField(autofill::FormData* form,
- (void)dealloc {
if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
webState_ = nullptr;
......@@ -232,6 +242,7 @@ void GetFormAndField(autofill::FormData* form,
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
webState_ = nullptr;
......@@ -505,51 +516,6 @@ void GetFormAndField(autofill::FormData* form,
pendingFormJSON_ = nil;
}
- (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame {
if (!isMainFrame) {
// Saving from iframes is not implemented.
return;
}
if (![self isAutofillEnabled])
return;
__weak AutofillAgent* weakSelf = self;
id completionHandler = ^(BOOL success, const FormDataVector& forms) {
AutofillAgent* strongSelf = weakSelf;
if (!strongSelf || !success)
return;
autofill::AutofillManager* autofillManager =
[strongSelf autofillManagerFromWebState:webState];
if (!autofillManager || forms.empty())
return;
if (forms.size() > 1) {
DLOG(WARNING) << "Only one form should be extracted.";
return;
}
[strongSelf notifyAutofillManager:autofillManager
ofFormsSubmitted:forms
userInitiated:userInitiated];
};
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
[self fetchFormsFiltered:YES
withName:base::UTF8ToUTF16(formName)
minimumRequiredFieldsCount:1
pageURL:pageURL
completionHandler:completionHandler];
}
- (void)webState:(web::WebState*)webState
didStartNavigation:(web::NavigationContext*)navigation {
// Ignore navigations within the same document, e.g., history.pushState().
......@@ -620,8 +586,11 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler];
}
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params {
registeredFormActivity:(const web::FormActivityParams&)params {
if (![self isAutofillEnabled])
return;
......@@ -681,6 +650,51 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler];
}
- (void)webState:(web::WebState*)webState
submittedDocumentWithFormNamed:(const std::string&)formName
hasUserGesture:(BOOL)hasUserGesture
formInMainFrame:(BOOL)formInMainFrame {
if (!formInMainFrame) {
// Saving from iframes is not implemented.
return;
}
if (![self isAutofillEnabled])
return;
__weak AutofillAgent* weakSelf = self;
id completionHandler = ^(BOOL success, const FormDataVector& forms) {
AutofillAgent* strongSelf = weakSelf;
if (!strongSelf || !success)
return;
autofill::AutofillManager* autofillManager =
[strongSelf autofillManagerFromWebState:webState];
if (!autofillManager || forms.empty())
return;
if (forms.size() > 1) {
DLOG(WARNING) << "Only one form should be extracted.";
return;
}
[strongSelf notifyAutofillManager:autofillManager
ofFormsSubmitted:forms
userInitiated:hasUserGesture];
};
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
[self fetchFormsFiltered:YES
withName:base::UTF8ToUTF16(formName)
minimumRequiredFieldsCount:1
pageURL:pageURL
completionHandler:completionHandler];
}
#pragma mark - PrefObserverDelegate
- (void)onPreferenceChanged:(const std::string&)preferenceName {
......
......@@ -50,6 +50,7 @@ source_set("autofill") {
"//base:i18n",
"//components/autofill/core/browser",
"//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/keyed_service/core",
"//components/keyed_service/ios",
"//components/prefs",
......@@ -123,6 +124,7 @@ source_set("unit_tests") {
"//components/autofill/core/browser:test_support",
"//components/autofill/core/common",
"//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/infobars/core",
"//components/keyed_service/core",
"//components/security_state/ios",
......
......@@ -11,6 +11,7 @@
#include "base/mac/scoped_block.h"
#import "components/autofill/core/browser/keyboard_accessory_metrics_logger.h"
#import "components/autofill/ios/browser/js_suggestion_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view_provider.h"
#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
......@@ -124,7 +125,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
} // namespace
@interface FormInputAccessoryViewController ()
@interface FormInputAccessoryViewController ()<FormActivityObserver>
// Allows injection of the JsSuggestionManager.
- (instancetype)initWithWebState:(web::WebState*)webState
......@@ -192,6 +193,10 @@ NSArray* FindDescendantToolbarItemsForActionName(
// Logs UMA metrics for the keyboard accessory.
std::unique_ptr<autofill::KeyboardAccessoryMetricsLogger>
_keyboardAccessoryMetricsLogger;
// Bridge to observe form activity in |_webState|.
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
}
@synthesize grayBackgroundView = _grayBackgroundView;
......@@ -222,6 +227,8 @@ NSArray* FindDescendantToolbarItemsForActionName(
_webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(_webState, self);
_providers = [providers copy];
_suggestionsHaveBeenShown = NO;
_keyboardAccessoryMetricsLogger.reset(
......@@ -241,6 +248,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
- (void)dealloc {
if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset();
_webState = nullptr;
......@@ -260,6 +268,8 @@ NSArray* FindDescendantToolbarItemsForActionName(
_webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(_webState, self);
_providers = @[ FormSuggestionTabHelper::FromWebState(_webState)
->GetAccessoryViewProvider() ];
......@@ -273,6 +283,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
- (void)detachFromWebState {
[self reset];
if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset();
_webState = nullptr;
......@@ -453,6 +464,29 @@ NSArray* FindDescendantToolbarItemsForActionName(
completionHandler];
}
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState
registeredFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState);
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
if (params.input_missing ||
trustLevel != web::URLVerificationTrustLevel::kAbsolute ||
!web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
[self reset];
return;
}
if (params.type == "blur" || params.type == "change" ||
params.type == "form_changed") {
return;
}
[self retrieveAccessoryViewForForm:params webState:webState];
}
#pragma mark -
#pragma mark CRWWebStateObserver
......@@ -503,26 +537,6 @@ NSArray* FindDescendantToolbarItemsForActionName(
[self reset];
}
- (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState);
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
if (params.input_missing ||
trustLevel != web::URLVerificationTrustLevel::kAbsolute ||
!web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
[self reset];
return;
}
if (params.type == "blur" || params.type == "change" ||
params.type == "form_changed") {
return;
}
[self retrieveAccessoryViewForForm:params webState:webState];
}
- (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(_webState, webState);
[self detachFromWebState];
......
......@@ -12,15 +12,17 @@
#include "base/strings/stringprintf.h"
#import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/form_suggestion_provider.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
#import "ios/chrome/browser/autofill/form_suggestion_view.h"
#include "ios/chrome/browser/ui/ui_util.h"
#import "ios/chrome/browser/web/chrome_web_test.h"
#import "ios/web/public/navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/web_state/form_activity_params.h"
#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
#import "ios/web/public/web_state/web_state.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
......@@ -135,12 +137,12 @@ FormSuggestionView* GetSuggestionView(UIView* parent) {
}
// Test fixture for FormSuggestionController testing.
class FormSuggestionControllerTest : public ChromeWebTest {
class FormSuggestionControllerTest : public PlatformTest {
public:
FormSuggestionControllerTest() {}
void SetUp() override {
ChromeWebTest::SetUp();
PlatformTest::SetUp();
// Mock out the JsSuggestionManager.
mock_js_suggestion_manager_ =
......@@ -159,16 +161,12 @@ class FormSuggestionControllerTest : public ChromeWebTest {
[OCMockObject niceMockForProtocol:@protocol(CRWWebViewProxy)];
[[[mock_web_view_proxy_ stub] andReturn:input_accessory_view_]
keyboardAccessory];
test_web_state_.SetWebViewProxy(mock_web_view_proxy_);
}
void TearDown() override {
[suggestion_controller_ detachFromWebState];
ChromeWebTest::TearDown();
}
// Sets |url| to be current for WebState.
void SetCurrentUrl(const std::string& url) {
LoadHtml(@"<html></html>", GURL(url));
PlatformTest::TearDown();
}
protected:
......@@ -176,13 +174,13 @@ class FormSuggestionControllerTest : public ChromeWebTest {
// FormSuggestionProviders.
void SetUpController(NSArray* providers) {
suggestion_controller_ = [[FormSuggestionController alloc]
initWithWebState:web_state()
initWithWebState:&test_web_state_
providers:providers
JsSuggestionManager:mock_js_suggestion_manager_];
[suggestion_controller_ setWebViewProxy:mock_web_view_proxy_];
@autoreleasepool {
accessory_controller_ = [[FormInputAccessoryViewController alloc]
initWithWebState:web_state()
initWithWebState:&test_web_state_
JSSuggestionManager:mock_js_suggestion_manager_
providers:@[
[suggestion_controller_ accessoryViewProvider]
......@@ -242,15 +240,17 @@ class FormSuggestionControllerTest : public ChromeWebTest {
// Accessory view controller.
FormInputAccessoryViewController* accessory_controller_;
// The fake WebState to simulate navigation and JavaScript events.
web::TestWebState test_web_state_;
DISALLOW_COPY_AND_ASSIGN(FormSuggestionControllerTest);
};
// Tests that pages whose URLs don't have a web scheme aren't processed.
TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) {
SetUpController(@[]);
SetCurrentUrl("data:text/html;charset=utf8;base64,");
[suggestion_controller_ webState:web_state() didLoadPageWithSuccess:YES];
test_web_state_.SetCurrentURL(GURL("data:text/html;charset=utf8;base64,"));
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
}
......@@ -258,24 +258,10 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) {
// Tests that pages whose content isn't HTML aren't processed.
TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) {
SetUpController(@[]);
// Construct file:// URL for a PDF file.
base::FilePath path;
base::PathService::Get(base::DIR_MODULE, &path);
const char kPdfFilePath[] = "ios/testing/data/http_server_files/testpage.pdf";
path = path.Append(FILE_PATH_LITERAL(kPdfFilePath));
GURL url(base::StringPrintf("file://%s", path.value().c_str()));
// Load PDF file URL.
web::NavigationManager::WebLoadParams params(url);
web_state()->GetNavigationManager()->LoadURLWithParams(params);
WaitForCondition(^{
return !web_state()->IsLoading();
});
ASSERT_EQ("application/pdf", web_state()->GetContentsMimeType());
test_web_state_.SetContentIsHTML(false);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
}
// Tests that the keyboard accessory view is reset and JavaScript is injected
......@@ -283,12 +269,7 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) {
TEST_F(FormSuggestionControllerTest,
PageLoadShouldRestoreKeyboardAccessoryViewAndInjectJavaScript) {
SetUpController(@[]);
SetCurrentUrl("http://foo.com");
// Load the page. The JS should be injected.
[[mock_js_suggestion_manager_ expect] inject];
[suggestion_controller_ webState:web_state() didLoadPageWithSuccess:YES];
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
test_web_state_.SetCurrentURL(GURL("http://foo.com"));
// Trigger form activity, which should set up the suggestions view.
web::FormActivityParams params;
......@@ -299,12 +280,12 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type";
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
EXPECT_TRUE(GetSuggestionView(input_accessory_view_));
// Trigger another page load. The suggestions accessory view should
// not be present.
[accessory_controller_ webState:web_state() didLoadPageWithSuccess:YES];
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
}
......@@ -318,7 +299,7 @@ TEST_F(FormSuggestionControllerTest, FormActivityBlurShouldBeIgnored) {
params.type = "blur"; // blur!
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
}
......@@ -327,7 +308,7 @@ TEST_F(FormSuggestionControllerTest,
FormActivityShouldRetrieveSuggestions_NoProvidersAvailable) {
// Set up the controller without any providers.
SetUpController(@[]);
SetCurrentUrl("http://foo.com");
test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params;
params.form_name = "form";
params.field_name = "field";
......@@ -336,7 +317,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type";
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
// The suggestions accessory view should be empty.
FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_);
......@@ -355,7 +336,7 @@ TEST_F(FormSuggestionControllerTest,
TestSuggestionProvider* provider2 =
[[TestSuggestionProvider alloc] initWithSuggestions:@[]];
SetUpController(@[ provider1, provider2 ]);
SetCurrentUrl("http://foo.com");
test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params;
params.form_name = "form";
......@@ -365,7 +346,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type";
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
// The providers should each be asked if they have suggestions for the
// form in question.
......@@ -405,7 +386,7 @@ TEST_F(FormSuggestionControllerTest,
TestSuggestionProvider* provider2 =
[[TestSuggestionProvider alloc] initWithSuggestions:@[]];
SetUpController(@[ provider1, provider2 ]);
SetCurrentUrl("http://foo.com");
test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params;
params.form_name = "form";
......@@ -415,7 +396,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type";
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
// Since the first provider has suggestions available, it and only it
// should have been asked.
......@@ -446,7 +427,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) {
TestSuggestionProvider* provider =
[[TestSuggestionProvider alloc] initWithSuggestions:suggestions];
SetUpController(@[ provider ]);
SetCurrentUrl("http://foo.com");
test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params;
params.form_name = "form";
params.field_name = "field";
......@@ -455,7 +436,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) {
params.type = "type";
params.value = "value";
params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params];
test_web_state_.OnFormActivity(params);
// Selecting a suggestion should notify the delegate.
[suggestion_controller_ didSelectSuggestion:suggestions[0]];
......
......@@ -50,6 +50,7 @@ source_set("passwords") {
"//components/autofill/core/browser",
"//components/autofill/core/common",
"//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/browser_sync",
"//components/image_fetcher/core",
"//components/image_fetcher/ios",
......
......@@ -27,6 +27,7 @@
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/infobars/core/infobar_manager.h"
#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
......@@ -121,7 +122,9 @@ void LogSuggestionShown(PasswordSuggestionType type) {
@end
@interface PasswordController ()<FormSuggestionProvider, PasswordFormFiller>
@interface PasswordController ()<FormActivityObserver,
FormSuggestionProvider,
PasswordFormFiller>
// Parses the |jsonString| which contatins the password forms found on a web
// page to populate the |forms| vector.
......@@ -318,6 +321,10 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
// User credential waiting to be displayed in autosign-in snackbar, once tab
// becomes active.
std::unique_ptr<autofill::PasswordForm> pendingAutoSigninPasswordForm_;
// Bridge to observe form activity in |webState_|.
std::unique_ptr<autofill::FormActivityObserverBridge>
formActivityObserverBridge_;
}
@synthesize isWebStateDestroyed = _isWebStateDestroyed;
......@@ -353,6 +360,8 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
webStateObserverBridge_ =
std::make_unique<web::WebStateObserverBridge>(self);
webState_->AddObserver(webStateObserverBridge_.get());
formActivityObserverBridge_ =
std::make_unique<autofill::FormActivityObserverBridge>(webState_, self);
passwordJsManager_ = [[JsPasswordManager alloc]
initWithReceiver:webState_->GetJSInjectionReceiver()];
sentRequestToStore_ = NO;
......@@ -382,6 +391,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
[self detach];
if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
webState_ = nullptr;
......@@ -400,6 +410,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
- (void)detach {
if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveScriptCommandCallback(kCommandPrefix);
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
......@@ -444,6 +455,39 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
}];
}
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState
submittedDocumentWithFormNamed:(const std::string&)formName
hasUserGesture:(BOOL)hasUserGesture
formInMainFrame:(BOOL)formInMainFrame {
DCHECK_EQ(webState_, webState);
__weak PasswordController* weakSelf = self;
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
id completionHandler = ^(BOOL found, const autofill::PasswordForm& form) {
PasswordController* strongSelf = weakSelf;
if (!strongSelf || [strongSelf isWebStateDestroyed] ||
!strongSelf.passwordManager) {
return;
}
if (formInMainFrame) {
strongSelf.passwordManager->OnPasswordFormSubmitted(
strongSelf.passwordManagerDriver, form);
} else {
// Show a save prompt immediately because for iframes it is very hard to
// figure out correctness of password forms submission.
strongSelf.passwordManager->OnPasswordFormSubmittedNoChecks(
strongSelf.passwordManagerDriver, form);
}
};
[self extractSubmittedPasswordForm:formName
completionHandler:completionHandler];
}
#pragma mark -
#pragma mark CRWWebStateObserver
......@@ -492,35 +536,6 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
[self findPasswordFormsAndSendThemToPasswordStore];
}
- (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame {
DCHECK_EQ(webState_, webState);
__weak PasswordController* weakSelf = self;
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
id completionHandler = ^(BOOL found, const autofill::PasswordForm& form) {
PasswordController* strongSelf = weakSelf;
if (strongSelf && ![strongSelf isWebStateDestroyed] &&
strongSelf.passwordManager) {
if (isMainFrame) {
strongSelf.passwordManager->OnPasswordFormSubmitted(
strongSelf.passwordManagerDriver, form);
} else {
// Show a save prompt immediately because for iframes it is very hard to
// figure out correctness of password forms submission.
strongSelf.passwordManager->OnPasswordFormSubmittedNoChecks(
strongSelf.passwordManagerDriver, form);
}
}
};
[self extractSubmittedPasswordForm:formName
completionHandler:completionHandler];
}
- (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(webState_, webState);
_isWebStateDestroyed = YES;
......
......@@ -213,6 +213,7 @@ ios_web_view_deps = [
"//components/autofill/core/browser",
"//components/autofill/core/common",
"//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/content_settings/core/browser",
"//components/flags_ui",
"//components/image_fetcher/ios",
......
......@@ -16,6 +16,7 @@
#include "components/autofill/ios/browser/autofill_driver_ios_bridge.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/keyed_service/core/service_access_type.h"
#include "ios/web/public/web_state/form_activity_params.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
......@@ -34,7 +35,8 @@
@interface CWVAutofillController ()<AutofillDriverIOSBridge,
CRWWebStateObserver,
CWVAutofillClientIOSBridge>
CWVAutofillClientIOSBridge,
FormActivityObserver>
@end
......@@ -63,6 +65,9 @@
// The current credit card verifier. Can be nil if no verification is pending.
// Held weak because |_delegate| is responsible for maintaing its lifetime.
__weak CWVCreditCardVerifier* _verifier;
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
}
@synthesize delegate = _delegate;
......@@ -85,6 +90,9 @@
std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(webState, self);
_autofillClient.reset(new autofill::WebViewAutofillClientIOS(
browserState->GetPrefs(),
ios_web_view::WebViewPersonalDataManagerFactory::GetForBrowserState(
......@@ -111,6 +119,7 @@
- (void)dealloc {
if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset();
_webState = nullptr;
......@@ -332,7 +341,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
#pragma mark - CRWWebStateObserver
- (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params {
registeredFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState);
[_JSSuggestionManager inject];
......@@ -376,9 +385,9 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
}
- (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame {
submittedDocumentWithFormNamed:(const std::string&)formName
hasUserGesture:(BOOL)userInitiated
formInMainFrame:(BOOL)isMainFrame {
if ([_delegate respondsToSelector:@selector
(autofillController:didSubmitFormWithName:userInitiated
:isMainFrame:)]) {
......@@ -391,6 +400,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(_webState, webState);
_formActivityObserverBridge.reset();
[_autofillAgent detachFromWebState];
_autofillClient.reset();
_webState->RemoveObserver(_webStateObserverBridge.get());
......
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