Commit a87a4dd3 authored by Maria Kazinova's avatar Maria Kazinova Committed by Commit Bot

Enabled PasswordManager to process dynamically added forms on iOS.

Done via enabling FormActivityObserver for PasswordController.
Now forms are reparsed if a new form appears or if there is a change
in an already existing form.

Bug: 904268
Change-Id: Idc0b0f767bae2e869cb3f99d292165264b636992
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2082387
Commit-Queue: Maria Kazinova <kazinova@google.com>
Reviewed-by: default avatarVadym Doroshenko <dvadym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748159}
parent 3f46a864
......@@ -167,6 +167,7 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/autofill/core/common",
"//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/password_manager/core/browser",
"//components/password_manager/core/browser:test_support",
"//components/password_manager/core/browser/leak_detection:leak_detection_interface_headers",
......
......@@ -29,6 +29,7 @@
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#include "components/autofill/ios/form_util/form_activity_params.h"
#include "components/infobars/core/infobar_manager.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
......@@ -75,6 +76,7 @@
#error "This file requires ARC support."
#endif
using autofill::FormActivityObserverBridge;
using autofill::FormData;
using autofill::PasswordFormGenerationData;
using autofill::PasswordForm;
......@@ -136,7 +138,9 @@ NSString* const kSuggestionSuffix = @" ••••••••";
@end
@interface PasswordController ()<FormSuggestionProvider, PasswordFormFiller>
@interface PasswordController () <FormSuggestionProvider,
PasswordFormFiller,
FormActivityObserver>
// Informs the |_passwordManager| of the password forms (if any were present)
// that have been found on the page.
......@@ -179,6 +183,9 @@ NSString* const kSuggestionSuffix = @" ••••••••";
// Bridge to observe WebState from Objective-C.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
// Bridge to observe form activity in |_webState|.
std::unique_ptr<FormActivityObserverBridge> _formActivityObserverBridge;
// Timer for hiding "Signing in as ..." notification.
base::OneShotTimer _notifyAutoSigninTimer;
......@@ -208,6 +215,8 @@ NSString* const kSuggestionSuffix = @" ••••••••";
_webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<FormActivityObserverBridge>(_webState, self);
_formHelper =
[[PasswordFormHelper alloc] initWithWebState:webState delegate:self];
_suggestionHelper =
......@@ -330,6 +339,7 @@ NSString* const kSuggestionSuffix = @" ••••••••";
if (_webState) {
_webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset();
_formActivityObserverBridge.reset();
_webState = nullptr;
}
_passwordManagerDriver.reset();
......@@ -969,4 +979,26 @@ NSString* const kSuggestionSuffix = @" ••••••••";
completionHandler:generatedPasswordInjected];
}
#pragma mark - FormActivityObserver
- (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const autofill::FormActivityParams&)params
inFrame:(web::WebFrame*)frame {
DCHECK_EQ(_webState, webState);
if (!GetPageURLAndCheckTrustLevel(webState, nullptr))
return;
if (!frame || !frame->CanCallJavaScriptFunction())
return;
// Return early if |params| is not complete or if forms are not changed.
if (params.input_missing || params.type != "form_changed")
return;
// If there's a change in password forms on a page, they should be parsed
// again.
[self findPasswordFormsAndSendThemToPasswordStore];
}
@end
......@@ -21,8 +21,10 @@
#include "components/autofill/core/browser/logging/log_buffer_submitter.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/ios/form_util/form_activity_params.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_form_manager.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
......@@ -188,8 +190,9 @@ ACTION(InvokeEmptyConsumerWithForms) {
} // namespace
@interface PasswordController (
Testing)<CRWWebStateObserver, FormSuggestionProvider>
@interface PasswordController (Testing) <CRWWebStateObserver,
FormSuggestionProvider,
FormActivityObserver>
// Provides access to common form helper logic for testing with mocks.
@property(readonly) PasswordFormHelper* formHelper;
......@@ -1780,3 +1783,29 @@ TEST_F(PasswordControllerTest, NoSavingOnNavigateMainFrameFailedSubmission) {
// Simulate a failed submission by loading the same form again.
LoadHtml(SysUTF8ToNSString(kHtml));
}
// Tests that a form that is dynamically added to the page is found and
// that a form manager is created for it.
TEST_F(PasswordControllerTest, FindDynamicallyAddedForm2) {
LoadHtml(kHtmlWithoutPasswordForm);
ExecuteJavaScript(kAddFormDynamicallyScript);
std::string mainFrameID = web::GetMainWebFrameId(web_state());
web::WebFrame* frame = web::GetWebFrameWithId(web_state(), mainFrameID);
autofill::FormActivityParams params;
params.type = "form_changed";
params.frame_id = mainFrameID;
[passwordController_ webState:web_state()
didRegisterFormActivity:params
inFrame:frame];
auto& form_managers = passwordController_.passwordManager->form_managers();
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool() {
return !form_managers.empty();
}));
ASSERT_EQ(1u, form_managers.size());
auto& password_form = form_managers[0]->observed_form();
EXPECT_EQ(ASCIIToUTF16("dynamic_form"), password_form.name);
}
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