#include "components/autofill/content/renderer/password_autofill_agent.h"
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "chrome/renderer/autofill/fake_mojo_password_manager_driver.h"
#include "chrome/renderer/autofill/fake_password_generation_driver.h"
#include "chrome/renderer/autofill/password_generation_test_utils.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/autofill_agent_test_api.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_tracker.h"
#include "components/autofill/content/renderer/password_generation_agent.h"
#include "components/autofill/content/renderer/test_password_autofill_agent.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_test_utils.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/password_manager/core/common/password_manager_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/safe_browsing/buildflags.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/test/browser_test_utils.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gtest/include/gtest/gtest-param-test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_form_element.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "ui/events/keycodes/keyboard_codes.h"
#if BUILDFLAG(IS_WIN)
#include "third_party/blink/public/web/win/web_font_rendering.h"
#endif
namespace autofill {
namespace {
using autofill::FormRendererId;
using autofill::FormTracker;
using autofill::mojom::FocusedFieldType;
using autofill::mojom::SubmissionIndicatorEvent;
using base::ASCIIToUTF16;
using base::UTF16ToUTF8;
using blink::WebAutofillState;
using blink::WebDocument;
using blink::WebElement;
using blink::WebFormElement;
using blink::WebFrame;
using blink::WebInputElement;
using blink::WebLocalFrame;
using blink::WebString;
using testing::_;
using testing::AllOf;
using testing::AtMost;
using testing::Eq;
using testing::Field;
using testing::Optional;
using testing::ResultOf;
using testing::Truly;
const char kUsernameName[] = "username";
const char kPasswordName[] = "password";
const char kSearchField[] = "search";
const char kSocialMediaTextArea[] = "new_chirp";
const char kAliceUsername[] = "alice";
const char16_t kAliceUsername16[] = u"alice";
const char kAlicePassword[] = "password";
const char16_t kAlicePassword16[] = u"password";
const char kBobUsername[] = "bob";
const char16_t kBobUsername16[] = u"bob";
const char kBobPassword[] = "secret";
const char16_t kBobPassword16[] = u"secret";
const char16_t kCarolUsername16[] = u"Carol";
const char kCarolPassword[] = "test";
const char16_t kCarolPassword16[] = u"test";
const char16_t kCarolAlternateUsername16[] = u"RealCarolUsername";
const char kFormHTML[] =
"<FORM id='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='random_field'/>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kSocialNetworkPostFormHTML[] =
"<FORM id='SocialMediaPostForm' action='http://www.chirper.com'>"
" <TEXTAREA id='new_chirp'>"
" </TEXTAREA>"
" <INPUT type='submit' value='Chirp'/>"
"</FORM>";
const char kSearchFieldHTML[] =
"<FORM id='SearchFieldForm' action='http://www.gewgle.de'>"
" <INPUT type='search' id='search'/>"
" <INPUT type='submit' value='Chirp'/>"
"</FORM>";
const char kWebAutnFieldHTML[] =
"<FORM id='WebAuthnFieldForm' action='http://www.gewgle.de'>"
" <INPUT type='text' id='username' autocomplete='webauthn'/>"
" <INPUT type='password' id='password' autocomplete='webauthn'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kVisibleFormWithNoUsernameHTML[] =
"<head> <style> form {display: inline;} </style> </head>"
"<body>"
" <form name='LoginTestForm' action='http://www.bidule.com'>"
" <div>"
" <input type='password' id='password'/>"
" </div>"
" </form>"
"</body>";
const char kSingleUsernameFormHTML[] =
"<FORM name='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username' autocomplete='username'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kSingleTextInputFormHTML[] =
"<FORM name='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kEmptyFormHTML[] =
"<head> <style> form {display: inline;} </style> </head>"
"<body> <form> </form> </body>";
const char kFormWithoutPasswordsHTML[] =
"<FORM>"
" <INPUT type='text' id='username'/>"
" <INPUT type='text' id='random_field'/>"
"</FORM>";
const char kNonVisibleFormHTML[] =
"<head> <style> form {visibility: hidden;} </style> </head>"
"<body>"
" <form>"
" <div>"
" <input type='password' id='password'/>"
" </div>"
" </form>"
"</body>";
const char kNonDisplayedFormHTML[] =
"<head> <style> form {display: none;} </style> </head>"
"<body>"
" <form>"
" <div>"
" <input type='password' id='password'/>"
" </div>"
" </form>"
"</body>";
const char kSignupFormHTML[] =
"<FORM id='LoginTestForm' name='LoginTestForm' "
" action='http://www.bidule.com'>"
" <INPUT type='text' id='random_info'/>"
" <INPUT type='password' id='new_password'/>"
" <INPUT type='password' id='confirm_password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kEmptyWebpage[] =
"<html>"
" <head>"
" </head>"
" <body>"
" </body>"
"</html>";
const char kRedirectionWebpage[] =
"<html>"
" <head>"
" <meta http-equiv='Content-Type' content='text/html'>"
" <title>Redirection page</title>"
" <script></script>"
" </head>"
" <body>"
" <script type='text/javascript'>"
" function test(){}"
" </script>"
" </body>"
"</html>";
const char kSimpleWebpage[] =
"<html>"
" <head>"
" <meta charset='utf-8' />"
" <title>Title</title>"
" </head>"
" <body>"
" <form name='LoginTestForm'>"
" <input type='text' id='username'/>"
" <input type='checkbox' id='accept-tc'>"
" <input type='password' id='password'/>"
" <input type='checkbox' id='remember-me'>"
" <input type='submit' value='Login'/>"
" </form>"
" </body>"
"</html>";
const char kWebpageWithDynamicContent[] =
"<html>"
" <head>"
" <meta charset='utf-8' />"
" <title>Title</title>"
" </head>"
" <body>"
" <script type='text/javascript'>"
" function addParagraph() {"
" var p = document.createElement('p');"
" document.body.appendChild(p);"
" }"
" window.onload = addParagraph;"
" </script>"
" </body>"
"</html>";
const char kJavaScriptClick[] =
"var event = new MouseEvent('click', {"
" 'view': window,"
" 'bubbles': true,"
" 'cancelable': true"
"});"
"var form = document.getElementById('myform1');"
"form.dispatchEvent(event);"
"console.log('clicked!');";
const char kPasswordChangeFormHTML[] =
"<FORM name='ChangeWithUsernameForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='password' id='newpassword'/>"
" <INPUT type='password' id='confirmpassword'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kPasswordChangeWithoutFormHTML[] =
"<DIV>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='password' id='newpassword'/>"
" <INPUT type='password' id='confirmpassword'/>"
" <INPUT type='submit' value='Change pwd'/>"
"</DIV>";
const char kCreditCardFormHTML[] =
"<FORM name='ChangeWithUsernameForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='creditcardowner'/>"
" <INPUT type='text' id='creditcardnumber'/>"
" <INPUT type='password' id='cvc'/>"
" <INPUT type='submit' value='Submit'/>"
"</FORM>";
const char kNoFormHTML[] =
"<script>"
" function on_keypress(event) {"
" if (event.which === 13) {"
" var field = document.getElementById('password');"
" field.parentElement.removeChild(field);"
" }"
" }"
"</script>"
"<INPUT type='text' id='username'/>"
"<INPUT type='password' id='password' onkeypress='on_keypress(event)'/>";
const char kTwoNoUsernameFormsHTML[] =
"<FORM name='form1' action='http://www.bidule.com'>"
" <INPUT type='password' id='password1' name='password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>"
"<FORM name='form2' action='http://www.bidule.com'>"
" <INPUT type='password' id='password2' name='password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
const char kDivWrappedFormHTML[] =
"<DIV id='outer'>"
" <DIV id='inner'>"
" <FORM id='form' action='http://www.bidule.com'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" </FORM>"
" </DIV>"
"</DIV>";
const char kJavaScriptRemoveForm[] =
"var form = document.getElementById('LoginTestForm');"
"form.parentNode.removeChild(form);";
const char kFormTagHostsShadowDomInputs[] =
"<script>"
" function addShadowFields() {"
" const un_host = document.getElementById('un_host');"
" const un_shadow = un_host.attachShadow({ mode: 'open'});"
" const un = document.createElement('input');"
" un_shadow.appendChild(un);"
" const pw_host = document.getElementById('pw_host');"
" const pw_shadow = pw_host.attachShadow({ mode: 'open'});"
" const pw = document.createElement('input');"
" pw.type = 'password';"
" pw_shadow.appendChild(pw);"
"}"
"</script>"
"<body onload='addShadowFields();'>"
"<form method='POST' action='done.html' id='shadyform'>"
"<div id='un_host'></div>"
"<div id='pw_host'></div>"
"<input type='submit' id='input_submit_button'>"
"</form>"
"</body>";
constexpr std::string_view kUnownedFieldsWithPasswordDisabled =
"<input type='text' id='username'>"
"<input type='password' disabled id='password'>";
void SetElementReadOnly(WebInputElement& element, bool read_only) {
element.SetAttribute(WebString::FromUTF8("readonly"),
read_only ? WebString::FromUTF8("true") : WebString());
}
bool FormHasFieldWithValue(const autofill::FormData& form,
const std::u16string& value) {
for (const auto& field : form.fields()) {
if (field.value() == value) {
return true;
}
if (field.user_input() == value) {
return true;
}
}
return false;
}
enum PasswordFormSourceType {
PasswordFormSubmitted,
PasswordFormSameDocumentNavigation,
};
enum class FieldChangeSource {
USER,
AUTOFILL_SINGLE_FIELD,
USER_AUTOFILL_SINGLE_FIELD,
AUTOFILL_FORM,
USER_AUTOFILL_FORM
};
auto NumShowSuggestionsCalls() {
if constexpr (BUILDFLAG(IS_ANDROID)) {
return base::FeatureList::IsEnabled(
features::kAutofillAndroidDisableSuggestionsOnJSFocus)
? testing::Exactly(1)
: testing::AtLeast(1);
}
return testing::Exactly(1);
}
class PasswordAutofillAgentTest : public ChromeRenderViewTest {
public:
PasswordAutofillAgentTest() = default;
PasswordAutofillAgentTest(const PasswordAutofillAgentTest&) = delete;
PasswordAutofillAgentTest& operator=(const PasswordAutofillAgentTest&) =
delete;
void SimulateOnFillPasswordForm(const PasswordFormFillData& fill_data) {
password_autofill_agent_->ApplyFillDataOnParsingCompletion(fill_data);
}
void SendVisiblePasswordForms() {
static_cast<content::RenderFrameObserver*>(password_autofill_agent_)
->DidFinishLoad();
}
void SetUp() override {
ChromeRenderViewTest::SetUp();
#if BUILDFLAG(IS_WIN)
blink::WebFontRendering::SetMenuFontMetrics(
blink::WebString::FromASCII("Arial"), 12);
#endif
password_autofill_agent_->GetPasswordManagerDriver();
password_generation_->RequestPasswordManagerClientForTesting();
base::RunLoop().RunUntilIdle();
GetMainRenderFrame()
->GetRemoteAssociatedInterfaces()
->OverrideBinderForTesting(
mojom::PasswordGenerationDriver::Name_,
base::BindRepeating([](mojo::ScopedInterfaceEndpointHandle handle) {
handle.reset();
}));
GetMainRenderFrame()
->GetRemoteAssociatedInterfaces()
->OverrideBinderForTesting(
mojom::PasswordManagerDriver::Name_,
base::BindRepeating([](mojo::ScopedInterfaceEndpointHandle handle) {
handle.reset();
}));
username1_ = kAliceUsername16;
password1_ = kAlicePassword16;
username2_ = kBobUsername16;
password2_ = kBobPassword16;
username3_ = kCarolUsername16;
password3_ = kCarolPassword16;
alternate_username3_ = kCarolAlternateUsername16;
fill_data_.preferred_login.username_value = username1_;
fill_data_.preferred_login.password_value = password1_;
PasswordAndMetadata password2;
password2.password_value = password2_;
password2.username_value = username2_;
fill_data_.additional_logins.push_back(std::move(password2));
PasswordAndMetadata password3;
password3.password_value = password3_;
password3.username_value = username3_;
fill_data_.additional_logins.push_back(std::move(password3));
UpdateUrlForHTML(kFormHTML);
LoadHTML(kFormHTML);
GetWebFrameWidget()->Resize(gfx::Size(500, 500));
GetWebFrameWidget()->SetFocus(true);
UpdateUsernameAndPasswordElements();
}
void TearDown() override {
username_element_.Reset();
password_element_.Reset();
ChromeRenderViewTest::TearDown();
}
void RegisterMainFrameRemoteInterfaces() override {
blink::AssociatedInterfaceProvider* remote_associated_interfaces =
GetMainRenderFrame()->GetRemoteAssociatedInterfaces();
remote_associated_interfaces->OverrideBinderForTesting(
mojom::PasswordGenerationDriver::Name_,
base::BindRepeating(
&PasswordAutofillAgentTest::BindPasswordManagerClient,
base::Unretained(this)));
remote_associated_interfaces->OverrideBinderForTesting(
mojom::PasswordManagerDriver::Name_,
base::BindRepeating(
&PasswordAutofillAgentTest::BindPasswordManagerDriver,
base::Unretained(this)));
}
void FocusElement(const std::string& element_id) {
std::string script =
"document.getElementById('" + element_id + "').focus()";
ExecuteJavaScriptForTests(script.c_str());
GetMainFrame()->NotifyUserActivation(
blink::mojom::UserActivationNotificationType::kTest);
GetMainFrame()->Client()->FocusedElementChanged(GetElementByID(element_id));
GetMainFrame()->AutofillClient()->DidCompleteFocusChangeInFrame();
}
void FocusFirstInputElement() {
ExecuteJavaScriptForTests("document.forms[0].elements[0].focus();");
GetMainFrame()->NotifyUserActivation(
blink::mojom::UserActivationNotificationType::kTest);
auto first_form_element =
GetMainFrame()->GetDocument().GetTopLevelForms()[0];
GetMainFrame()->Client()->FocusedElementChanged(
first_form_element.GetFormControlElements()[0]);
GetMainFrame()->AutofillClient()->DidCompleteFocusChangeInFrame();
}
void BlurElement(const std::string& element_id) {
std::string script = "document.getElementById('" + element_id + "').blur()";
ExecuteJavaScriptForTests(script.c_str());
ChangeFocusToNull(GetMainFrame()->GetDocument());
}
void ConfigurePasswordSuggestionFiltering(bool enabled) {
if (enabled) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kNoPasswordSuggestionFiltering);
} else {
scoped_feature_list_.InitAndDisableFeature(
password_manager::features::kNoPasswordSuggestionFiltering);
}
}
void EnableShowAutofillSignatures() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kShowAutofillSignatures);
}
void UpdateUrlForHTML(const std::string& html) {
std::string url = "data:text/html;charset=utf-8," + html;
fill_data_.url = GURL(url);
}
void UpdateRendererIDsInFillData() {
fill_data_.username_element_renderer_id =
username_element_
? autofill::form_util::GetFieldRendererId(username_element_)
: autofill::FieldRendererId();
fill_data_.password_element_renderer_id =
password_element_
? autofill::form_util::GetFieldRendererId(password_element_)
: autofill::FieldRendererId();
ASSERT_TRUE(username_element_ || password_element_);
WebFormElement form =
password_element_ ? password_element_.Form() : username_element_.Form();
fill_data_.form_renderer_id = form_util::GetFormRendererId(form);
}
void UpdateUsernameAndPasswordElements() {
username_element_ = GetInputElementByID(kUsernameName);
password_element_ = GetInputElementByID(kPasswordName);
UpdateRendererIDsInFillData();
}
void UpdateOnlyUsernameElement() {
username_element_ = GetInputElementByID(kUsernameName);
password_element_.Reset();
UpdateRendererIDsInFillData();
}
void UpdateOnlyPasswordElement() {
username_element_.Reset();
password_element_ = GetInputElementByID(kPasswordName);
UpdateRendererIDsInFillData();
}
WebElement GetElementByID(const std::string& id) {
WebDocument document = GetMainFrame()->GetDocument();
WebElement element =
document.GetElementById(WebString::FromUTF8(id.c_str()));
EXPECT_TRUE(element);
return element;
}
WebInputElement GetInputElementByID(const std::string& id) {
WebInputElement input_element = GetElementByID(id).To<WebInputElement>();
EXPECT_TRUE(input_element);
return input_element;
}
void ClearUsernameAndPasswordFieldValues() {
if (username_element_) {
username_element_.SetValue(WebString());
username_element_.SetSuggestedValue(WebString());
username_element_.SetAutofillState(WebAutofillState::kNotFilled);
}
if (password_element_) {
password_element_.SetValue(WebString());
password_element_.SetSuggestedValue(WebString());
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
}
}
void SimulateElementClick(const WebElement element) {
SimulatePointClick(element.BoundsInWidget().CenterPoint());
}
using ChromeRenderViewTest::SimulateElementClick;
void SimulateSuggestionChoice(WebInputElement& username_input) {
std::u16string username(kAliceUsername16);
std::u16string password(kAlicePassword16);
SimulateSuggestionChoiceOfUsernameAndPassword(username_input, username,
password);
}
void SimulateSuggestionChoiceOfUsernameAndPassword(
WebInputElement& input,
const std::u16string& username,
const std::u16string& password) {
SimulatePointClick(gfx::Point(1, 1));
SimulateElementClick(input);
password_autofill_agent_->FillPasswordSuggestion(username, password,
base::DoNothing());
}
void SimulateUsernameTyping(const std::string& username) {
SimulatePointClick(gfx::Point(1, 1));
#if BUILDFLAG(IS_ANDROID)
FocusElement(kUsernameName);
#endif
SimulateUserInputChangeForElement(username_element_, username);
}
void SimulatePasswordTyping(const std::string& password) {
#if BUILDFLAG(IS_ANDROID)
FocusElement(kPasswordName);
#endif
SimulateUserInputChangeForElement(password_element_, password);
}
void SimulateUsernameSingleFieldAutofill(const std::u16string& text) {
FocusElement(kUsernameName);
autofill_agent_->ApplyFieldAction(
mojom::FieldActionType::kReplaceAll, mojom::ActionPersistence::kFill,
form_util::GetFieldRendererId(username_element_), text);
}
void SimulateUsernameFormAutofill(const std::u16string& text) {
FocusElement(kUsernameName);
std::vector<autofill::FormFieldData::FillData> fields;
FormFieldData::FillData field;
field.value = text;
field.is_autofilled = true;
field.renderer_id = form_util::GetFieldRendererId(username_element_);
field.host_form_id = form_util::GetFormRendererId(username_element_.Form());
fields.push_back(field);
autofill_agent_->ApplyFieldsAction(mojom::FormActionType::kFill,
mojom::ActionPersistence::kFill, fields);
}
void SimulateUsernameFieldChange(FieldChangeSource change_source) {
switch (change_source) {
case FieldChangeSource::USER:
SimulateUsernameTyping("Alice");
break;
case FieldChangeSource::AUTOFILL_SINGLE_FIELD:
SimulateUsernameSingleFieldAutofill(u"Alice");
break;
case FieldChangeSource::USER_AUTOFILL_SINGLE_FIELD:
SimulateUsernameTyping("A");
SimulateUsernameSingleFieldAutofill(u"Alice");
break;
case FieldChangeSource::AUTOFILL_FORM:
SimulateUsernameFormAutofill(u"Alice");
break;
case FieldChangeSource::USER_AUTOFILL_FORM:
SimulateUsernameTyping("A");
SimulateUsernameFormAutofill(u"Alice");
break;
}
}
void SimulateClosingKeyboardReplacingSurfaceIfAndroid(
const std::string& element_id) {
#if BUILDFLAG(IS_ANDROID)
FocusElement(element_id);
#endif
}
void CheckTextFieldsStateForElements(const WebInputElement& username_element,
const std::string& username,
bool username_autofilled,
const WebInputElement& password_element,
const std::string& password,
bool password_autofilled,
bool check_suggested_username,
bool check_suggested_password) {
if (username_element) {
EXPECT_EQ(username, check_suggested_username
? username_element.SuggestedValue().Utf8()
: username_element.Value().Utf8())
<< "check_suggested_username == " << check_suggested_username;
EXPECT_EQ(username_autofilled, username_element.IsPreviewed() ||
username_element.IsAutofilled());
}
if (password_element) {
EXPECT_EQ(password, check_suggested_password
? password_element.SuggestedValue().Utf8()
: password_element.Value().Utf8())
<< "check_suggested_password == " << check_suggested_password;
EXPECT_EQ(password_autofilled, password_element.IsAutofilled() ||
password_element.IsPreviewed());
}
}
void CheckUsernameDOMStatePasswordSuggestedState(const std::string& username,
bool username_autofilled,
const std::string& password,
bool password_autofilled) {
CheckTextFieldsStateForElements(
username_element_, username, username_autofilled, password_element_,
password, password_autofilled, false ,
true );
}
void CheckTextFieldsDOMState(const std::string& username,
bool username_autofilled,
const std::string& password,
bool password_autofilled) {
CheckTextFieldsStateForElements(
username_element_, username, username_autofilled, password_element_,
password, password_autofilled, false ,
false );
}
void CheckTextFieldsSuggestedState(const std::string& username,
bool username_autofilled,
const std::string& password,
bool password_autofilled) {
CheckTextFieldsStateForElements(
username_element_, username, username_autofilled, password_element_,
password, password_autofilled, true ,
true );
}
void ResetFieldState(
WebInputElement* element,
const std::string& value = std::string(),
blink::WebAutofillState is_autofilled = WebAutofillState::kNotFilled) {
element->SetValue(WebString::FromUTF8(value));
element->SetSuggestedValue(WebString());
element->SetAutofillState(is_autofilled);
element->SetSelectionRange(value.size(), value.size());
}
void CheckUsernameSelection(unsigned start, unsigned end) {
EXPECT_EQ(start, username_element_.SelectionStart());
EXPECT_EQ(end, username_element_.SelectionEnd());
}
void CheckSuggestions(const std::u16string& typed_username,
bool show_all,
base::Location location = FROM_HERE) {
std::u16string expected_username = show_all ? u"" : typed_username;
SCOPED_TRACE(testing::Message()
<< __func__ << " called from " << location.ToString());
EXPECT_CALL(fake_driver_,
ShowPasswordSuggestions(AllOf(
Field(&autofill::PasswordSuggestionRequest::field,
Field(&autofill::TriggeringField::typed_username,
expected_username)))))
.Times(NumShowSuggestionsCalls());
base::RunLoop().RunUntilIdle();
}
void CheckSuggestionsNotShown() {
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
base::RunLoop().RunUntilIdle();
}
void ExpectFieldPropertiesMasks(
PasswordFormSourceType expected_type,
const std::map<std::u16string, FieldPropertiesMask>&
expected_properties_masks,
autofill::mojom::SubmissionIndicatorEvent expected_submission_event) {
base::RunLoop().RunUntilIdle();
autofill::FormData form_data;
if (expected_type == PasswordFormSubmitted) {
ASSERT_TRUE(fake_driver_.called_password_form_submitted());
ASSERT_TRUE(static_cast<bool>(fake_driver_.form_data_submitted()));
form_data = *(fake_driver_.form_data_submitted());
} else {
ASSERT_EQ(PasswordFormSameDocumentNavigation, expected_type);
ASSERT_TRUE(fake_driver_.called_dynamic_form_submission());
ASSERT_TRUE(static_cast<bool>(fake_driver_.form_data_maybe_submitted()));
form_data = *(fake_driver_.form_data_maybe_submitted());
EXPECT_EQ(expected_submission_event, form_data.submission_event());
}
size_t unchecked_masks = expected_properties_masks.size();
for (const FormFieldData& field : form_data.fields()) {
const auto& it = expected_properties_masks.find(field.name());
if (it == expected_properties_masks.end())
continue;
EXPECT_EQ(field.properties_mask(), it->second)
<< "Wrong mask for the field " << field.name();
unchecked_masks--;
}
EXPECT_TRUE(unchecked_masks == 0)
<< "Some expected masks are missed in FormData";
}
FormRendererId GetFormUniqueRendererId(const WebString& form_id) {
WebLocalFrame* frame = GetMainFrame();
if (!frame)
return FormRendererId();
WebFormElement web_form =
frame->GetDocument().GetElementById(form_id).To<WebFormElement>();
return form_util::GetFormRendererId(web_form);
}
void ExpectFormDataWithUsernameAndPasswordsAndEvent(
const autofill::FormData& form_data,
FormRendererId form_renderer_id,
base::optional_ref<const std::u16string> username_value,
base::optional_ref<const std::u16string> password_value,
base::optional_ref<const std::u16string> new_password_value,
SubmissionIndicatorEvent event) {
EXPECT_EQ(form_renderer_id, form_data.renderer_id());
if (username_value) {
EXPECT_TRUE(FormHasFieldWithValue(form_data, *username_value));
}
if (password_value) {
EXPECT_TRUE(FormHasFieldWithValue(form_data, *password_value));
}
if (new_password_value) {
EXPECT_TRUE(FormHasFieldWithValue(form_data, *new_password_value));
}
EXPECT_EQ(form_data.submission_event(), event);
}
void ExpectFormSubmittedWithUsernameAndPasswords(
FormRendererId form_renderer_id,
std::optional<std::u16string> username_value,
std::optional<std::u16string> password_value,
std::optional<std::u16string> new_password_value = std::nullopt) {
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_driver_.called_password_form_submitted());
ASSERT_TRUE(static_cast<bool>(fake_driver_.form_data_submitted()));
ExpectFormDataWithUsernameAndPasswordsAndEvent(
*(fake_driver_.form_data_submitted()), form_renderer_id, username_value,
password_value, new_password_value,
SubmissionIndicatorEvent::HTML_FORM_SUBMISSION);
}
void ExpectDynamicFormSubmissionWithUsernameAndPasswords(
FormRendererId form_renderer_id,
const std::u16string& username_value,
const std::u16string& password_value,
SubmissionIndicatorEvent event) {
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_driver_.called_dynamic_form_submission());
ASSERT_TRUE(static_cast<bool>(fake_driver_.form_data_maybe_submitted()));
ExpectFormDataWithUsernameAndPasswordsAndEvent(
*(fake_driver_.form_data_maybe_submitted()), form_renderer_id,
username_value, password_value, std::nullopt, event);
}
void CheckIfEventsAreCalled(const std::vector<std::u16string>& checkers,
bool expected) {
for (const std::u16string& variable : checkers) {
int value;
EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(variable, &value))
<< variable;
EXPECT_EQ(expected, value == 1) << variable;
}
}
void BindPasswordManagerDriver(mojo::ScopedInterfaceEndpointHandle handle) {
fake_driver_.BindReceiver(
mojo::PendingAssociatedReceiver<mojom::PasswordManagerDriver>(
std::move(handle)));
}
void BindPasswordManagerClient(mojo::ScopedInterfaceEndpointHandle handle) {
fake_pw_client_.BindReceiver(
mojo::PendingAssociatedReceiver<mojom::PasswordGenerationDriver>(
std::move(handle)));
}
void SaveAndSubmitForm() { SaveAndSubmitForm(username_element_.Form()); }
void SaveAndSubmitForm(const WebFormElement& form_element) {
FormTracker& tracker = test_api(*autofill_agent_).form_tracker();
static_cast<blink::WebLocalFrameObserver&>(tracker).WillSendSubmitEvent(
form_element);
static_cast<content::RenderFrameObserver&>(tracker).WillSubmitForm(
form_element);
}
void CheckFirstFillingResult(FillingResult result) {
histogram_tester_.ExpectUniqueSample(
"PasswordManager.FirstRendererFillingResult", result, 1);
}
void SubmitForm() {
FormTracker& tracker = test_api(*autofill_agent_).form_tracker();
static_cast<content::RenderFrameObserver&>(tracker).WillSubmitForm(
username_element_.Form());
}
void FireAjaxSucceeded() {
FormTracker& tracker = test_api(*autofill_agent_).form_tracker();
tracker.AjaxSucceeded();
}
void FireDidFinishSameDocumentNavigation() {
FormTracker& tracker = test_api(*autofill_agent_).form_tracker();
static_cast<content::RenderFrameObserver&>(tracker)
.DidFinishSameDocumentNavigation();
}
::testing::AssertionResult UpdateFormElementsForFormHostingShadowDom() {
username_element_ = GetElementByID("un_host")
.ShadowRoot()
.FirstChild()
.To<WebInputElement>();
if (!username_element_) {
return ::testing::AssertionFailure() << "Username element is null.";
}
password_element_ = GetElementByID("pw_host")
.ShadowRoot()
.FirstChild()
.To<WebInputElement>();
if (!password_element_) {
return ::testing::AssertionFailure() << "Password element is null.";
}
return ::testing::AssertionSuccess();
}
void ForceLayoutUpdate() {
GetWebFrameWidget()->UpdateAllLifecyclePhases(
blink::DocumentUpdateReason::kTest);
}
FakeMojoPasswordManagerDriver fake_driver_;
testing::NiceMock<FakePasswordGenerationDriver> fake_pw_client_;
std::u16string username1_;
std::u16string username2_;
std::u16string username3_;
std::u16string password1_;
std::u16string password2_;
std::u16string password3_;
std::u16string alternate_username3_;
PasswordFormFillData fill_data_;
WebInputElement username_element_;
WebInputElement password_element_;
base::test::ScopedFeatureList scoped_feature_list_;
protected:
base::HistogramTester histogram_tester_;
};
TEST_F(PasswordAutofillAgentTest, InitialAutocomplete) {
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForEmptyAction) {
const char kEmptyActionFormHTML[] =
"<FORM name='LoginTestForm'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
LoadHTML(kEmptyActionFormHTML);
UpdateUsernameAndPasswordElements();
UpdateUrlForHTML(kEmptyActionFormHTML);
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForReadOnlyPassword) {
SetElementReadOnly(password_element_, true);
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(std::string(), false, std::string(), false);
CheckFirstFillingResult(FillingResult::kPasswordElementIsNotAutocompleteable);
}
TEST_F(PasswordAutofillAgentTest,
AutocompletePasswordForReadonlyUsernameMatched) {
username_element_.SetValue(WebString::FromUTF16(username3_));
SetElementReadOnly(username_element_, true);
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(UTF16ToUTF8(username3_), false,
UTF16ToUTF8(password3_), true);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest, AutocompleteForPrefilledUsernameValue) {
username_element_.SetValue(WebString::FromUTF16(u" User Name "));
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest, MetricsOnlyLoggedOnce) {
username_element_.SetValue(WebString::FromUTF16(u" User Name "));
SimulateOnFillPasswordForm(fill_data_);
SimulateOnFillPasswordForm(fill_data_);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest,
AutocompletePasswordForReadonlyUsernamePrefixMatched) {
std::u16string username_at = username3_ + u"@example.com";
username_element_.SetValue(WebString::FromUTF16(username_at));
SetElementReadOnly(username_element_, true);
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(UTF16ToUTF8(username_at), false,
UTF16ToUTF8(password3_), true);
}
TEST_F(PasswordAutofillAgentTest, NoFillingOnSignupForm_NoMetrics) {
LoadHTML(kSignupFormHTML);
WebDocument document = GetMainFrame()->GetDocument();
WebElement element =
document.GetElementById(WebString::FromUTF8("random_info"));
ASSERT_TRUE(element);
username_element_ = element.To<WebInputElement>();
fill_data_.username_element_renderer_id = autofill::FieldRendererId();
fill_data_.password_element_renderer_id = autofill::FieldRendererId();
WebFormElement form_element =
document.GetElementById("LoginTestForm").To<WebFormElement>();
fill_data_.form_renderer_id = form_util::GetFormRendererId(form_element);
SimulateOnFillPasswordForm(fill_data_);
histogram_tester_.ExpectTotalCount(
"PasswordManager.FirstRendererFillingResult", 0);
}
TEST_F(PasswordAutofillAgentTest,
DontAutocompletePasswordForReadonlyUsernamePrefixMatched) {
std::u16string prefilled_username = username3_ + u"example.com";
username_element_.SetValue(WebString::FromUTF16(prefilled_username));
SetElementReadOnly(username_element_, true);
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(UTF16ToUTF8(prefilled_username),
false, std::string(), false);
CheckFirstFillingResult(
FillingResult::kUsernamePrefilledWithIncompatibleValue);
}
TEST_F(
PasswordAutofillAgentTest,
DontAutocompletePasswordForNotReadonlyUsernameFieldEvenWhenPrefixMatched) {
std::u16string prefilled_username = username3_ + u"@example.com";
username_element_.SetValue(WebString::FromUTF16(prefilled_username));
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(UTF16ToUTF8(prefilled_username),
false, std::string(), false);
}
TEST_F(PasswordAutofillAgentTest,
NoAutocompletePasswordForReadonlyUsernameUnmatched) {
username_element_.SetValue(WebString::FromUTF8(""));
SetElementReadOnly(username_element_, true);
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(std::string(), false,
std::string(), false);
CheckFirstFillingResult(FillingResult::kFoundNoPasswordForUsername);
}
TEST_F(PasswordAutofillAgentTest, NoAutocompleteForFilledFieldUnmatched) {
username_element_.SetValue(WebString::FromUTF8("bogus"));
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState("bogus", false, std::string(),
false);
CheckFirstFillingResult(
FillingResult::kUsernamePrefilledWithIncompatibleValue);
}
TEST_F(PasswordAutofillAgentTest, NoPartialMatchForPrefilledUsername) {
username_element_.SetValue(WebString::FromUTF8("ali"));
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState("", false, std::string(), false);
CheckUsernameDOMStatePasswordSuggestedState("ali", false, std::string(),
false);
}
TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForMatchingFilledField) {
username_element_.SetValue(WebString::FromUTF16(kAliceUsername16));
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState(kAliceUsername, false,
kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest, PasswordNotClearedOnEdit) {
SimulateOnFillPasswordForm(fill_data_);
SimulateUsernameTyping("alicia");
CheckTextFieldsDOMState("alicia", false, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, WaitUsername) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(
std::string(),
false,
std::string(),
false);
SimulateUsernameTyping(kAliceUsername);
SetFocused(password_element_);
SetFocused(username_element_);
CheckUsernameDOMStatePasswordSuggestedState(
kAliceUsername,
false,
std::string(),
false);
CheckFirstFillingResult(FillingResult::kWaitForUsername);
}
TEST_F(PasswordAutofillAgentTest, IsWebElementVisibleTest) {
blink::WebLocalFrame* frame;
LoadHTML(kVisibleFormWithNoUsernameHTML);
frame = GetMainFrame();
std::vector<WebFormElement> forms = frame->GetDocument().GetTopLevelForms();
ASSERT_EQ(1u, forms.size());
std::vector<blink::WebFormControlElement> web_control_elements =
forms[0].GetFormControlElements();
ASSERT_EQ(1u, web_control_elements.size());
EXPECT_TRUE(web_control_elements[0].IsFocusable());
LoadHTML(kNonVisibleFormHTML);
frame = GetMainFrame();
forms = frame->GetDocument().GetTopLevelForms();
ASSERT_EQ(1u, forms.size());
web_control_elements = forms[0].GetFormControlElements();
ASSERT_EQ(1u, web_control_elements.size());
EXPECT_FALSE(web_control_elements[0].IsFocusable());
LoadHTML(kNonDisplayedFormHTML);
frame = GetMainFrame();
forms = frame->GetDocument().GetTopLevelForms();
ASSERT_EQ(1u, forms.size());
web_control_elements = forms[0].GetFormControlElements();
ASSERT_EQ(1u, web_control_elements.size());
EXPECT_FALSE(web_control_elements[0].IsFocusable());
}
TEST_F(PasswordAutofillAgentTest,
SendPasswordFormsTest_VisibleFormWithNoUsername) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kVisibleFormWithNoUsernameHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_FALSE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_EmptyForm) {
base::RunLoop().RunUntilIdle();
fake_driver_.reset_password_forms_calls();
LoadHTML(kEmptyFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_TRUE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_FormWithoutPasswords) {
base::RunLoop().RunUntilIdle();
fake_driver_.reset_password_forms_calls();
LoadHTML(kFormWithoutPasswordsHTML);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_TRUE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest,
SendPasswordFormsTest_UndetectedPasswordField) {
base::RunLoop().RunUntilIdle();
fake_driver_.reset_password_forms_calls();
LoadHTML(kFormWithoutPasswordsHTML);
std::string script =
"document.getElementById('random_field').type = 'password';";
ExecuteJavaScriptForTests(script.c_str());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
EXPECT_TRUE(SimulateElementClick("random_field"));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_TRUE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_NonDisplayedForm) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kNonDisplayedFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_TRUE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_NonVisibleForm) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kNonVisibleFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_TRUE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_PasswordChangeForm) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kPasswordChangeFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_FALSE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest,
SendPasswordFormsTest_CannotCreatePasswordForm) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kCreditCardFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
ASSERT_TRUE(fake_driver_.form_data_rendered());
EXPECT_FALSE(fake_driver_.form_data_rendered()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_ReloadTab) {
fake_driver_.reset_password_forms_calls();
LoadHTML(kNonVisibleFormHTML);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
fake_driver_.reset_password_forms_calls();
std::string url_string = "data:text/html;charset=utf-8,";
url_string.append(kNonVisibleFormHTML);
Reload(GURL(url_string));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
ASSERT_TRUE(fake_driver_.form_data_parsed());
EXPECT_FALSE(fake_driver_.form_data_parsed()->empty());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_Redirection) {
base::RunLoop().RunUntilIdle();
fake_driver_.reset_password_forms_calls();
LoadHTML(kEmptyWebpage);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_password_forms_rendered());
fake_driver_.reset_password_forms_calls();
LoadHTML(kRedirectionWebpage);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_password_forms_rendered());
fake_driver_.reset_password_forms_calls();
LoadHTML(kSimpleWebpage);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
fake_driver_.reset_password_forms_calls();
LoadHTML(kWebpageWithDynamicContent);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
}
TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_UnownedtextInputs) {
fake_driver_.reset_password_forms_calls();
const char kFormlessFieldsNonPasswordHTML[] =
" <INPUT type='text' name='email'>"
" <INPUT type='submit' value='Login'/>";
LoadHTML(kFormlessFieldsNonPasswordHTML);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_password_forms_parsed());
fake_driver_.reset_password_forms_calls();
const char kFormlessFieldsPasswordHTML[] =
" <INPUT type='text' name='email'>"
" <INPUT type='password' name='pw'>"
" <INPUT type='submit' value='Login'/>";
LoadHTML(kFormlessFieldsPasswordHTML);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_driver_.called_password_forms_parsed());
}
TEST_F(PasswordAutofillAgentTest, GestureRequiredTest) {
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckTextFieldsDOMState(std::string(), true, std::string(), true);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, NoDOMActivationTest) {
SimulateOnFillPasswordForm(fill_data_);
ExecuteJavaScriptForTests(kJavaScriptClick);
CheckTextFieldsDOMState("", true, "", true);
}
TEST_F(PasswordAutofillAgentTest,
PasswordAutofillTriggersOnChangeEventsOnLoad) {
std::vector<std::u16string> username_event_checkers;
std::vector<std::u16string> password_event_checkers;
std::string events_registration_script =
CreateScriptToRegisterListeners(kUsernameName, &username_event_checkers) +
CreateScriptToRegisterListeners(kPasswordName, &password_event_checkers);
std::string html = std::string(kFormHTML) + events_registration_script;
LoadHTML(html.c_str());
UpdateUrlForHTML(html);
UpdateUsernameAndPasswordElements();
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckTextFieldsDOMState(std::string(), true, std::string(), true);
CheckIfEventsAreCalled(username_event_checkers, false);
CheckIfEventsAreCalled(password_event_checkers, false);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
CheckIfEventsAreCalled(username_event_checkers, true);
CheckIfEventsAreCalled(password_event_checkers, true);
}
TEST_F(PasswordAutofillAgentTest,
PasswordAutofillTriggersOnChangeEventsWaitForUsername) {
std::vector<std::u16string> event_checkers;
std::string events_registration_script =
CreateScriptToRegisterListeners(kUsernameName, &event_checkers) +
CreateScriptToRegisterListeners(kPasswordName, &event_checkers);
std::string html = std::string(kFormHTML) + events_registration_script;
LoadHTML(html.c_str());
UpdateUrlForHTML(html);
UpdateUsernameAndPasswordElements();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(std::string(), false, std::string(), false);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
SimulateUsernameTyping("a");
std::erase(event_checkers, u"username_blur_event");
SimulateSuggestionChoice(username_element_);
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
CheckIfEventsAreCalled(event_checkers, true);
}
TEST_F(PasswordAutofillAgentTest, FillSuggestionOnUsernameField) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
CheckTextFieldsDOMState(std::string(),
false,
std::string(),
false);
SetElementReadOnly(username_element_, true);
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(false));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, mock_reply.Get());
CheckTextFieldsDOMState(std::string(),
false,
std::string(),
false);
SetElementReadOnly(username_element_, false);
SetElementReadOnly(password_element_, true);
EXPECT_CALL(mock_reply, Run(false));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, mock_reply.Get());
CheckTextFieldsDOMState(kAliceUsername, true,
std::string(),
false);
size_t username_length = strlen(kAliceUsername);
CheckUsernameSelection(username_length, username_length);
SetElementReadOnly(password_element_, false);
ResetFieldState(&username_element_);
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, mock_reply.Get());
CheckTextFieldsDOMState(kAliceUsername, true,
kAlicePassword, true);
username_length = strlen(kAliceUsername);
CheckUsernameSelection(username_length, username_length);
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillPasswordSuggestion(
kBobUsername16, kCarolPassword16, mock_reply.Get());
CheckTextFieldsDOMState(kBobUsername, true,
kCarolPassword, true);
username_length = strlen(kBobUsername);
CheckUsernameSelection(username_length, username_length);
}
TEST_F(PasswordAutofillAgentTest,
NoFillSuggestionOnNoFormTagAndPasswordDisabled) {
LoadHTML(kUnownedFieldsWithPasswordDisabled);
UpdateUsernameAndPasswordElements();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, FillSuggestionOnPasswordField) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(password_element_);
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
SetElementReadOnly(password_element_, true);
base::MockCallback<base::OnceCallback<void(bool)>> reply_call;
EXPECT_CALL(reply_call, Run(false));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, reply_call.Get());
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
SetElementReadOnly(password_element_, false);
SetElementReadOnly(username_element_, true);
EXPECT_CALL(reply_call, Run(false));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, reply_call.Get());
CheckTextFieldsDOMState(std::string(),
false, kAlicePassword,
true);
SetElementReadOnly(username_element_, false);
ResetFieldState(&username_element_);
EXPECT_CALL(reply_call, Run(true));
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, reply_call.Get());
CheckTextFieldsDOMState(kAliceUsername, true,
kAlicePassword, true);
size_t username_length = strlen(kAliceUsername);
CheckUsernameSelection(username_length, username_length);
EXPECT_CALL(reply_call, Run(true));
password_autofill_agent_->FillPasswordSuggestion(
kBobUsername16, kCarolPassword16, reply_call.Get());
CheckTextFieldsDOMState(kBobUsername, true,
kCarolPassword, true);
username_length = strlen(kBobUsername);
CheckUsernameSelection(username_length, username_length);
}
TEST_F(PasswordAutofillAgentTest, FillSuggestionWithDynamicUsernameField) {
LoadHTML(kVisibleFormWithNoUsernameHTML);
UpdateOnlyPasswordElement();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
constexpr const char* kAddUsernameToFormScript =
"var new_input = document.createElement('input');"
"new_input.setAttribute('type', 'text');"
"new_input.setAttribute('id', 'username');"
"password_field = document.getElementById('password');"
"password_field.parentNode.insertBefore(new_input, password_field);";
ExecuteJavaScriptForTests(kAddUsernameToFormScript);
UpdateUsernameAndPasswordElements();
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
SimulateElementClick(password_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kWaitForUsername);
}
TEST_F(PasswordAutofillAgentTest,
FillSuggestionFromPasswordFieldWithUsernameManuallyFilled) {
username_element_.SetValue(WebString::FromUTF8("user1"));
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("user1", false, std::string(), false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
SimulateElementClick(password_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState("user1", false, kAlicePassword, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
password_autofill_agent_->FillPasswordSuggestion(
kBobUsername16, kCarolPassword16, base::DoNothing());
CheckTextFieldsDOMState("user1", false, kCarolPassword, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, PreviewSuggestionOnUsernameField) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
SetElementReadOnly(password_element_, true);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->PreviewSuggestion(
username_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(kAliceUsername, true,
std::string(),
false);
SetElementReadOnly(password_element_, false);
password_autofill_agent_->ClearPreviewedForm();
SetElementReadOnly(username_element_, true);
password_autofill_agent_->PreviewSuggestion(
username_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(
std::string(), false,
std::string(), false);
SetElementReadOnly(username_element_, false);
password_autofill_agent_->PreviewSuggestion(
username_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(kAliceUsername, true,
kAlicePassword, true);
CheckUsernameSelection(0, 0);
password_autofill_agent_->PreviewSuggestion(username_element_, kBobUsername16,
kCarolPassword16);
CheckTextFieldsSuggestedState(kBobUsername, true,
kCarolPassword, true);
CheckUsernameSelection(0, 0);
}
TEST_F(PasswordAutofillAgentTest, PreviewSuggestionOnPasswordField) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(std::string(),
false,
std::string(),
false);
SetElementReadOnly(password_element_, true);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->PreviewSuggestion(
password_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(
std::string(), false,
std::string(), false);
SetElementReadOnly(password_element_, false);
SetElementReadOnly(username_element_, true);
password_autofill_agent_->PreviewSuggestion(
password_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(std::string(),
false, kAlicePassword,
true);
SetElementReadOnly(username_element_, false);
password_autofill_agent_->PreviewSuggestion(
password_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(kAliceUsername, true,
kAlicePassword, true);
CheckUsernameSelection(0, 0);
password_autofill_agent_->PreviewSuggestion(password_element_, kBobUsername16,
kCarolPassword16);
CheckTextFieldsSuggestedState(kBobUsername, true,
kCarolPassword, true);
CheckUsernameSelection(0, 0);
}
TEST_F(PasswordAutofillAgentTest,
PreviewSuggestionFromPasswordFieldWithUsernameManuallyFilled) {
username_element_.SetValue(WebString::FromUTF8("user1"));
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("user1", false, std::string(), false);
ASSERT_TRUE(SimulateElementClick("password"));
password_autofill_agent_->PreviewSuggestion(
password_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(std::string(), false, kAlicePassword, true);
CheckTextFieldsDOMState("user1", false, std::string(), true);
password_autofill_agent_->PreviewSuggestion(password_element_, kBobUsername16,
kCarolPassword16);
CheckTextFieldsSuggestedState(std::string(), false, kCarolPassword, true);
CheckTextFieldsDOMState("user1", false, std::string(), true);
}
TEST_F(PasswordAutofillAgentTest, PreviewSuggestionSelectionRange) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
for (const auto& selected_element : {username_element_, password_element_}) {
ASSERT_TRUE(
SimulateElementClick(selected_element.GetAttribute("id").Ascii()));
ResetFieldState(&username_element_, "ali", WebAutofillState::kPreviewed);
ResetFieldState(&password_element_);
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckUsernameSelection(3, 3);
}
}
TEST_F(PasswordAutofillAgentTest, ClearPreviewWithPasswordAutofilled) {
ResetFieldState(&password_element_, "sec", WebAutofillState::kPreviewed);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(std::string(), false, "sec", true);
for (const auto& selected_element : {username_element_, password_element_}) {
ASSERT_TRUE(
SimulateElementClick(selected_element.GetAttribute("id").Ascii()));
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
CheckTextFieldsDOMState(std::string(), false, "sec", true);
CheckUsernameSelection(0, 0);
}
}
TEST_F(PasswordAutofillAgentTest, ClearPreviewWithUsernameAutofilled) {
ResetFieldState(&username_element_, "ali", WebAutofillState::kPreviewed);
username_element_.SetSelectionRange(3, 3);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("ali", true, std::string(), false);
for (const auto& selected_element : {username_element_, password_element_}) {
ASSERT_TRUE(
SimulateElementClick(selected_element.GetAttribute("id").Ascii()));
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
CheckTextFieldsDOMState("ali", true, std::string(), false);
CheckUsernameSelection(3, 3);
}
}
TEST_F(PasswordAutofillAgentTest, PreviewField) {
WebInputElement random_element = GetInputElementByID("random_field");
std::vector<WebInputElement> elements{username_element_, password_element_,
random_element};
for (WebInputElement& element : elements) {
SetElementReadOnly(element, true);
password_autofill_agent_->PreviewField(
form_util::GetFieldRendererId(element), kAliceUsername16);
EXPECT_TRUE(element.SuggestedValue().IsEmpty());
SetElementReadOnly(element, false);
password_autofill_agent_->PreviewField(
form_util::GetFieldRendererId(element), kAliceUsername16);
EXPECT_EQ(kAliceUsername, element.SuggestedValue().Utf8());
}
}
TEST_F(PasswordAutofillAgentTest, PreviewField_ClearPreviewedForm) {
WebInputElement random_element = GetInputElementByID("random_field");
std::vector<WebInputElement> elements{username_element_, password_element_,
random_element};
for (WebInputElement& element : elements) {
ResetFieldState(&element, "ali", WebAutofillState::kAutofilled);
element.SetSelectionRange(0u, 0u);
password_autofill_agent_->PreviewField(
form_util::GetFieldRendererId(element), kAliceUsername16);
EXPECT_EQ(kAliceUsername, element.SuggestedValue().Utf8());
EXPECT_TRUE(element.IsPreviewed());
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(element.SuggestedValue().IsEmpty());
EXPECT_TRUE(element.IsAutofilled());
EXPECT_EQ(0u, element.SelectionStart());
EXPECT_EQ(0u, element.SelectionEnd());
}
}
TEST_F(PasswordAutofillAgentTest,
ClearPreviewWithAutofilledUsernameAndPassword) {
ResetFieldState(&username_element_, "ali", WebAutofillState::kPreviewed);
ResetFieldState(&password_element_, "sec", WebAutofillState::kPreviewed);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("ali", true, "sec", true);
for (const auto& selected_element : {username_element_, password_element_}) {
ASSERT_TRUE(
SimulateElementClick(selected_element.GetAttribute("id").Ascii()));
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
CheckTextFieldsDOMState("ali", true, "sec", true);
CheckUsernameSelection(3, 3);
}
}
TEST_F(PasswordAutofillAgentTest, ClearPreviewBeforeFillingSuggestion) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("", false,
"", false);
for (const auto& selected_element : {username_element_, password_element_}) {
SetFocused(selected_element);
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(
kAliceUsername, true,
kAlicePassword, true);
CheckTextFieldsDOMState("", true,
"", true);
EXPECT_TRUE(username_element_.IsPreviewed());
EXPECT_TRUE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestion(
kBobUsername16, kBobPassword16, base::DoNothing());
CheckTextFieldsSuggestedState(
"", true, "",
true);
CheckTextFieldsDOMState(
kBobUsername, true,
kBobPassword, true);
EXPECT_TRUE(username_element_.IsAutofilled());
EXPECT_TRUE(password_element_.IsAutofilled());
password_autofill_agent_->ClearPreviewedForm();
CheckTextFieldsSuggestedState(
"", true, "",
true);
CheckTextFieldsDOMState(
kBobUsername, true,
kBobPassword, true);
EXPECT_TRUE(username_element_.IsAutofilled());
EXPECT_TRUE(password_element_.IsAutofilled());
ClearUsernameAndPasswordFieldValues();
}
}
TEST_F(PasswordAutofillAgentTest, FillIntoFocusedReadonlyTextField) {
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
SetElementReadOnly(username_element_, true);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->FillIntoFocusedField(
false, kAliceUsername16);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, FillIntoFocusedWritableTextField) {
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
FocusElement(kUsernameName);
SetElementReadOnly(username_element_, false);
password_autofill_agent_->FillIntoFocusedField(
false, kAliceUsername16);
CheckTextFieldsDOMState(kAliceUsername, true, std::string(), false);
CheckUsernameSelection(strlen(kAliceUsername), strlen(kAliceUsername));
}
TEST_F(PasswordAutofillAgentTest, FillIntoFocusedFieldOnlyIntoPasswordFields) {
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
FocusElement(kUsernameName);
password_autofill_agent_->FillIntoFocusedField(
true, kAlicePassword16);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
FocusElement(kPasswordName);
password_autofill_agent_->FillIntoFocusedField(
true, kAlicePassword16);
CheckTextFieldsDOMState(std::string(), false, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, FillIntoFocusedFieldForNonClickFocus) {
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
FocusElement(kPasswordName);
password_autofill_agent_->FillIntoFocusedField(
false, u"TextToFill");
CheckTextFieldsDOMState(std::string(), false, "TextToFill", true);
}
TEST_F(PasswordAutofillAgentTest, FillIntoReadonlyTextField) {
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
SetElementReadOnly(username_element_, true);
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(false));
password_autofill_agent_->FillField(
form_util::GetFieldRendererId(username_element_), kAliceUsername16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, FillIntoUsernameField_FlagOn) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kActorLoginTreatFillingAsUserInput);
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillField(
form_util::GetFieldRendererId(username_element_), kAliceUsername16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
CheckTextFieldsDOMState(
kAliceUsername, true,
std::string(), false);
EXPECT_TRUE(base::test::RunUntil([&]() {
return fake_driver_.called_inform_about_user_input_count() == 1;
}));
}
TEST_F(PasswordAutofillAgentTest, FillIntoUsernameField_FlagOff) {
scoped_feature_list_.InitAndDisableFeature(
password_manager::features::kActorLoginTreatFillingAsUserInput);
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillField(
form_util::GetFieldRendererId(username_element_), kAliceUsername16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
CheckTextFieldsDOMState(
kAliceUsername, true,
std::string(), false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, FillIntoPasswordField) {
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillField(
form_util::GetFieldRendererId(password_element_), kAlicePassword16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
CheckTextFieldsDOMState(
std::string(), false,
kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, FillIntoRandomField) {
WebInputElement random_element = GetInputElementByID("random_field");
EXPECT_EQ(std::string(), random_element.Value().Utf8());
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(true));
password_autofill_agent_->FillField(
form_util::GetFieldRendererId(random_element), kAliceUsername16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
EXPECT_EQ(kAliceUsername, random_element.Value().Utf8());
}
TEST_F(PasswordAutofillAgentTest, FillIntoNonExistingField) {
WebInputElement random_element = GetInputElementByID("random_field");
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
EXPECT_EQ(std::string(), random_element.Value().Utf8());
base::MockCallback<base::OnceCallback<void(bool)>> mock_reply;
EXPECT_CALL(mock_reply, Run(false));
password_autofill_agent_->FillField(
FieldRendererId(), kAliceUsername16,
autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger,
mock_reply.Get());
CheckTextFieldsDOMState(
std::string(), false,
std::string(), false);
EXPECT_EQ(std::string(), random_element.Value().Utf8());
}
TEST_F(PasswordAutofillAgentTest,
ClearPreviewWithNotAutofilledUsernameAndPassword) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
for (const auto& selected_element : {username_element_, password_element_}) {
ASSERT_TRUE(
SimulateElementClick(selected_element.GetAttribute("id").Ascii()));
password_autofill_agent_->PreviewSuggestion(
selected_element, kAliceUsername16, kAlicePassword16);
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
CheckUsernameSelection(0, 0);
}
}
TEST_F(PasswordAutofillAgentTest, OnChangeLoggingState_NoMessage) {
SendVisiblePasswordForms();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_record_save_progress());
}
TEST_F(PasswordAutofillAgentTest, OnChangeLoggingState_Activated) {
password_autofill_agent_->SetLoggingState(true);
SendVisiblePasswordForms();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_record_save_progress());
}
TEST_F(PasswordAutofillAgentTest, OnChangeLoggingState_Deactivated) {
password_autofill_agent_->SetLoggingState(true);
password_autofill_agent_->SetLoggingState(false);
SendVisiblePasswordForms();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_record_save_progress());
}
TEST_F(PasswordAutofillAgentTest, ClickAndSelect) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
FocusElement(kUsernameName);
ClearUsernameAndPasswordFieldValues();
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
base::RunLoop().RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"PasswordManager.SuggestionPopupTriggerSource",
static_cast<int>(autofill::AutofillSuggestionTriggerSource::
kFormControlElementClicked),
1);
SimulateSuggestionChoice(username_element_);
CheckSuggestions(kAliceUsername16, true);
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, SuggestionsPrefixMatchedByTypedUsername) {
ConfigurePasswordSuggestionFiltering(false);
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
ClearUsernameAndPasswordFieldValues();
SimulateOnFillPasswordForm(fill_data_);
SimulateUsernameTyping("ali");
SimulateElementClick(username_element_);
CheckSuggestions(u"ali", false);
base::RunLoop().RunUntilIdle();
}
TEST_F(PasswordAutofillAgentTest,
SuggestionsNotPrefixMatchedWhenFeatureEnabled) {
ConfigurePasswordSuggestionFiltering(true);
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
ClearUsernameAndPasswordFieldValues();
SimulateOnFillPasswordForm(fill_data_);
SimulateUsernameTyping("ali");
SimulateElementClick(username_element_);
CheckSuggestions(u"ali", true);
base::RunLoop().RunUntilIdle();
}
TEST_F(PasswordAutofillAgentTest,
NoPopupOnPasswordFieldWhereAddressOrPaymentsManualFallbackWasSelected) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateOnFillPasswordForm(fill_data_);
SimulatePointClick(gfx::Point(1, 1));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
autofill_agent_->TriggerSuggestions(
form_util::GetFieldRendererId(username_element_),
AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses);
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
autofill_agent_->TriggerSuggestions(
form_util::GetFieldRendererId(username_element_),
AutofillSuggestionTriggerSource::kManualFallbackPasswords);
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
TEST_F(
PasswordAutofillAgentTest,
NoPopupOnPasswordFieldWithoutSuggestionsByDefaultWhenNotEligibleForPromo) {
ClearUsernameAndPasswordFieldValues();
UpdateRendererIDsInFillData();
password_autofill_agent_->InformNoSavedCredentials(false);
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
}
TEST_F(PasswordAutofillAgentTest,
PopupOnPasswordFieldWithoutSuggestionsWhenEligibleForPromo) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
ClearUsernameAndPasswordFieldValues();
UpdateRendererIDsInFillData();
password_autofill_agent_->InformNoSavedCredentials(
true);
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
ASSERT_TRUE(SimulateElementClick(kPasswordName));
}
#endif
TEST_F(PasswordAutofillAgentTest, CredentialsOnClick) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateOnFillPasswordForm(fill_data_);
ClearUsernameAndPasswordFieldValues();
ASSERT_TRUE(SimulateElementClick(kUsernameName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
base::RunLoop().RunUntilIdle();
SimulateElementClick(username_element_);
CheckSuggestions(std::u16string(), true);
EXPECT_CALL(fake_driver_,
ShowPasswordSuggestions(Field(
&autofill::PasswordSuggestionRequest::field,
Field(&autofill::TriggeringField::element_id,
form_util::GetFieldRendererId(username_element_)))))
.Times(NumShowSuggestionsCalls());
SimulateUsernameTyping(kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, NoCredentialsOnPasswordClick) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
SimulateOnFillPasswordForm(fill_data_);
ClearUsernameAndPasswordFieldValues();
ASSERT_TRUE(SimulateElementClick(kUsernameName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
base::RunLoop().RunUntilIdle();
SimulateElementClick(password_element_);
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
base::RunLoop().RunUntilIdle();
}
TEST_F(PasswordAutofillAgentTest,
RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
LoadHTML(kSignupFormHTML);
WebInputElement username_element = GetInputElementByID("random_info");
ASSERT_TRUE(username_element);
SimulateUserInputChangeForElement(username_element, "username");
WebInputElement new_password_element = GetInputElementByID("new_password");
ASSERT_TRUE(new_password_element);
SimulateUserInputChangeForElement(new_password_element, "random");
WebInputElement confirmation_password_element =
GetInputElementByID("confirm_password");
ASSERT_TRUE(confirmation_password_element);
SimulateUserInputChangeForElement(confirmation_password_element, "random");
username_element.SetValue(WebString());
new_password_element.SetValue(WebString());
confirmation_password_element.SetValue(WebString());
FormTracker& tracker = test_api(*autofill_agent_).form_tracker();
static_cast<content::RenderFrameObserver&>(tracker).WillSubmitForm(
username_element.Form());
ExpectFormSubmittedWithUsernameAndPasswords(
form_util::GetFormRendererId(username_element.Form()), u"username",
std::nullopt, u"random");
fake_driver_.form_data_submitted();
}
TEST_F(PasswordAutofillAgentTest,
RememberLastNonEmptyUsernameAndPasswordOnSubmit_UserCleared) {
SimulateUsernameTyping("temp");
SimulatePasswordTyping("random");
SimulateUsernameTyping("");
SimulatePasswordTyping("");
SubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"",
u"");
}
TEST_F(PasswordAutofillAgentTest,
RememberLastNonEmptyUsernameAndPasswordOnSubmit_New) {
const char kNewPasswordFormHTML[] =
"<FORM name='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username' autocomplete='username'/>"
" <INPUT type='password' id='password' autocomplete='new-password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
LoadHTML(kNewPasswordFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("temp");
SimulatePasswordTyping("random");
username_element_.SetValue(WebString());
password_element_.SetValue(WebString());
SubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
form_util::GetFormRendererId(username_element_.Form()), u"temp",
std::nullopt, u"random");
}
TEST_F(PasswordAutofillAgentTest, RememberLastNonEmptySingleUsername) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
SimulateUsernameTyping("temp");
SubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
form_util::GetFormRendererId(username_element_.Form()), u"temp",
u"");
}
TEST_F(PasswordAutofillAgentTest,
NoopEditingDoesNotOverwriteManuallyEditedPassword) {
fill_data_.wait_for_username = true;
SimulateUsernameTyping(kAliceUsername);
SimulateOnFillPasswordForm(fill_data_);
SimulateSuggestionChoice(username_element_);
const std::string old_username(username_element_.Value().Utf8());
const std::string old_password(password_element_.Value().Utf8());
const std::string new_password(old_password + "modify");
SimulatePasswordTyping(new_password);
SetFocused(password_element_);
SetFocused(username_element_);
CheckTextFieldsDOMState(old_username, false, new_password, false);
CheckUsernameDOMStatePasswordSuggestedState(old_username, false,
std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, AcceptingSuggestionDoesntRewriteUsername) {
fill_data_.wait_for_username = true;
SimulateUsernameTyping(kAliceUsername);
SimulateOnFillPasswordForm(fill_data_);
SimulateSuggestionChoice(username_element_);
const std::string username(username_element_.Value().Utf8());
const std::string password(password_element_.Value().Utf8());
CheckTextFieldsDOMState(username, false, password, true);
}
TEST_F(PasswordAutofillAgentTest,
RememberLastTypedUsernameAndPasswordOnSubmit_ScriptChanged) {
SimulateUsernameTyping("temp");
SimulatePasswordTyping("random");
username_element_.SetValue(WebString("new username"));
password_element_.SetValue(WebString("new password"));
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"temp", u"random");
}
TEST_F(PasswordAutofillAgentTest, RememberFieldPropertiesOnSubmit) {
FocusElement("random_field");
SimulateUsernameTyping("typed_username");
SimulatePasswordTyping("typed_password");
username_element_.SetValue(WebString("new username"));
password_element_.SetValue(WebString("new password"));
SaveAndSubmitForm();
std::map<std::u16string, FieldPropertiesMask> expected_properties_masks;
expected_properties_masks[u"random_field"] = FieldPropertiesFlags::kHadFocus;
expected_properties_masks[u"username"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
expected_properties_masks[u"password"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
ExpectFieldPropertiesMasks(PasswordFormSubmitted, expected_properties_masks,
SubmissionIndicatorEvent::HTML_FORM_SUBMISSION);
}
TEST_F(PasswordAutofillAgentTest, FixEmptyFieldPropertiesOnSubmit) {
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
static constexpr char kJavaScript[] =
"const old_username = document.getElementById('username');"
"const old_password = document.getElementById('password');"
"const new_username = document.createElement('input');"
"new_username.value = old_username.value;"
"new_username.id = 'new_username';"
"const new_password = document.createElement('input');"
"new_password.value = old_password.value;"
"new_password.id = 'new_password';"
"const form = document.getElementById('LoginTestForm');"
"form.appendChild(new_username);"
"form.appendChild(new_password);"
"form.removeChild(old_username);"
"form.removeChild(old_password);";
ExecuteJavaScriptForTests(kJavaScript);
auto form_element = GetMainFrame()
->GetDocument()
.GetElementById(WebString::FromUTF8("LoginTestForm"))
.To<WebFormElement>();
SaveAndSubmitForm(form_element);
std::map<std::u16string, FieldPropertiesMask> expected_properties_masks;
expected_properties_masks[u"new_username"] =
FieldPropertiesFlags::kAutofilledOnPageLoad;
expected_properties_masks[u"new_password"] =
FieldPropertiesFlags::kAutofilledOnPageLoad;
ExpectFieldPropertiesMasks(PasswordFormSubmitted, expected_properties_masks,
SubmissionIndicatorEvent::HTML_FORM_SUBMISSION);
}
TEST_F(PasswordAutofillAgentTest,
RememberFieldPropertiesOnSameDocumentNavigation) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
FireAjaxSucceeded();
std::map<std::u16string, FieldPropertiesMask> expected_properties_masks;
expected_properties_masks[u"username"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
expected_properties_masks[u"password"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
ExpectFieldPropertiesMasks(PasswordFormSameDocumentNavigation,
expected_properties_masks,
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
RememberFieldPropertiesOnSameDocumentNavigation_2) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
ForceLayoutUpdate();
base::RunLoop().RunUntilIdle();
std::map<std::u16string, FieldPropertiesMask> expected_properties_masks;
expected_properties_masks[u"username"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
expected_properties_masks[u"password"] =
FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kHadFocus;
ExpectFieldPropertiesMasks(PasswordFormSameDocumentNavigation,
expected_properties_masks,
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
RememberLastAutofilledUsernameAndPasswordOnSubmit_ScriptChanged) {
SimulateOnFillPasswordForm(fill_data_);
username_element_.SetValue(WebString("new username"));
password_element_.SetValue(WebString("new password"));
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), kAliceUsername16,
kAlicePassword16);
}
TEST_F(
PasswordAutofillAgentTest,
RememberLastTypedAfterAutofilledUsernameAndPasswordOnSubmit_ScriptChanged) {
SimulateOnFillPasswordForm(fill_data_);
SimulateUsernameTyping("temp");
SimulatePasswordTyping("random");
username_element_.SetValue(WebString("new username"));
password_element_.SetValue(WebString("new password"));
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"temp", u"random");
}
TEST_F(PasswordAutofillAgentTest, RememberAutofilledUsername) {
SimulateUsernameTyping("Te");
username_element_.SetValue(WebString("temp"));
SimulatePasswordTyping("random");
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"temp", u"random");
}
TEST_F(PasswordAutofillAgentTest,
RememberUsernameGeneratedBasingOnTypedFields) {
SimulateUsernameTyping("Temp");
SimulatePasswordTyping("random");
WebInputElement surname_element = GetInputElementByID("random_field");
SimulateUserInputChangeForElement(surname_element, "Smith");
username_element_.SetValue(WebString("foo.smith"));
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"foo.smith", u"random");
}
TEST_F(PasswordAutofillAgentTest, DontFillFormWithNoUsername) {
LoadHTML(kVisibleFormWithNoUsernameHTML);
UpdateOnlyPasswordElement();
SimulateOnFillPasswordForm(fill_data_);
CheckFirstFillingResult(FillingResult::kFoundNoPasswordForUsername);
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById) {
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->PreviewPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16);
EXPECT_EQ(username_element_.SuggestedValue().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.IsPreviewed());
EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16, AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_EQ(username_element_.SelectionStart(), 5u);
EXPECT_EQ(username_element_.SelectionEnd(), 5u);
EXPECT_EQ(username_element_.Value().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.IsAutofilled());
EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_ReadOnlyElements) {
ASSERT_TRUE(SimulateElementClick(kUsernameName));
SetElementReadOnly(username_element_, true);
SetElementReadOnly(password_element_, true);
password_autofill_agent_->PreviewPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16);
EXPECT_EQ(username_element_.SuggestedValue().Utf16(), u"");
EXPECT_FALSE(username_element_.IsPreviewed());
EXPECT_EQ(password_element_.SuggestedValue().Utf16(), u"");
EXPECT_FALSE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16, AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_EQ(username_element_.Value().Utf16(), u"");
EXPECT_FALSE(username_element_.IsAutofilled());
EXPECT_EQ(password_element_.Value().Utf16(), u"");
EXPECT_FALSE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoUsernameValue) {
username_element_.SetValue(WebString(kBobUsername16));
ASSERT_TRUE(SimulateElementClick(kPasswordName));
password_autofill_agent_->PreviewPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), u"", kAlicePassword16);
EXPECT_EQ(username_element_.SuggestedValue().Utf16(), u"");
EXPECT_FALSE(username_element_.IsPreviewed());
EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), u"", kAlicePassword16,
AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_EQ(username_element_.Value().Utf16(), kBobUsername16);
EXPECT_FALSE(username_element_.IsAutofilled());
EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoUsername) {
LoadHTML(kVisibleFormWithNoUsernameHTML);
password_element_ = GetInputElementByID(kPasswordName);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
password_autofill_agent_->PreviewPasswordSuggestionById(
FieldRendererId(), form_util::GetFieldRendererId(password_element_),
kAliceUsername16, kAlicePassword16);
EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.Value().IsEmpty());
EXPECT_TRUE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
FieldRendererId(), form_util::GetFieldRendererId(password_element_),
kAliceUsername16, kAlicePassword16,
AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_TRUE(password_element_.SuggestedValue().IsEmpty());
EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoPassword) {
LoadHTML(kSingleUsernameFormHTML);
username_element_ = GetInputElementByID(kUsernameName);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->PreviewPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_), FieldRendererId(),
kAliceUsername16, kAlicePassword16);
EXPECT_EQ(username_element_.SuggestedValue().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.Value().IsEmpty());
EXPECT_TRUE(username_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_), FieldRendererId(),
kAliceUsername16, kAlicePassword16,
AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
EXPECT_EQ(username_element_.Value().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, FillPasswordSuggestionById_NoFocusedElement) {
ASSERT_TRUE(SimulateElementClick("random_field"));
password_autofill_agent_->PreviewPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16);
EXPECT_EQ(username_element_.SuggestedValue().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.IsPreviewed());
EXPECT_EQ(password_element_.SuggestedValue().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsPreviewed());
password_autofill_agent_->FillPasswordSuggestionById(
form_util::GetFieldRendererId(username_element_),
form_util::GetFieldRendererId(password_element_), kAliceUsername16,
kAlicePassword16, AutofillSuggestionTriggerSource::kUnspecified);
EXPECT_EQ(username_element_.Value().Utf16(), kAliceUsername16);
EXPECT_TRUE(username_element_.IsAutofilled());
EXPECT_EQ(password_element_.Value().Utf16(), kAlicePassword16);
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, ShowPopupOnEmptyPasswordField) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kVisibleFormWithNoUsernameHTML);
UpdateUrlForHTML(kVisibleFormWithNoUsernameHTML);
UpdateOnlyPasswordElement();
fill_data_.preferred_login.username_value.clear();
fill_data_.additional_logins.clear();
password_element_.SetValue("");
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
SimulateOnFillPasswordForm(fill_data_);
password_element_.SetValue("");
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
SimulateSuggestionChoiceOfUsernameAndPassword(
password_element_, std::u16string(), kAlicePassword16);
CheckSuggestions(std::u16string(), true);
EXPECT_EQ(kAlicePassword16, password_element_.Value().Utf16());
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, ShowPopupOnAutofilledPasswordField) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kVisibleFormWithNoUsernameHTML);
UpdateUrlForHTML(kVisibleFormWithNoUsernameHTML);
UpdateOnlyPasswordElement();
fill_data_.preferred_login.username_value.clear();
fill_data_.additional_logins.clear();
password_element_.SetValue("");
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
SimulateOnFillPasswordForm(fill_data_);
password_element_.SetValue("123");
password_element_.SetAutofillState(WebAutofillState::kAutofilled);
SimulateSuggestionChoiceOfUsernameAndPassword(
password_element_, std::u16string(), kAlicePassword16);
CheckSuggestions(std::u16string(), true);
EXPECT_EQ(kAlicePassword16, password_element_.Value().Utf16());
EXPECT_TRUE(password_element_.IsAutofilled());
}
TEST_F(PasswordAutofillAgentTest, NotShowPopupPasswordField) {
LoadHTML(kVisibleFormWithNoUsernameHTML);
UpdateUrlForHTML(kVisibleFormWithNoUsernameHTML);
UpdateOnlyPasswordElement();
fill_data_.preferred_login.username_value.clear();
fill_data_.additional_logins.clear();
password_element_.SetValue("");
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
SimulateOnFillPasswordForm(fill_data_);
password_element_.SetValue("123");
password_element_.SetAutofillState(WebAutofillState::kNotFilled);
SimulateSuggestionChoiceOfUsernameAndPassword(
password_element_, std::u16string(), kAlicePassword16);
CheckSuggestionsNotShown();
}
TEST_F(PasswordAutofillAgentTest,
FillOnAccountSelectOnlyReadonlyUnknownUsername) {
ClearUsernameAndPasswordFieldValues();
username_element_.SetValue("foobar");
SetElementReadOnly(username_element_, true);
CheckUsernameDOMStatePasswordSuggestedState(std::string("foobar"), false,
std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, ReadonlyPasswordFieldOnSubmit) {
SimulateUsernameTyping("temp");
SimulatePasswordTyping("random");
SetElementReadOnly(password_element_, true);
SubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"temp", u"random");
}
TEST_F(PasswordAutofillAgentTest, PasswordGenerationTriggered_TypedPassword) {
SimulateOnFillPasswordForm(fill_data_);
SetFoundFormEligibleForGeneration(
password_generation_, GetMainFrame()->GetDocument(),
"password", nullptr);
#if !BUILDFLAG(IS_ANDROID)
EXPECT_CALL(fake_pw_client_, GenerationElementLostFocus())
.Times(testing::AnyNumber());
#endif
SimulateUsernameTyping("NewGuy");
SimulatePasswordTyping("NewPassword");
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"NewGuy", u"NewPassword");
}
TEST_F(PasswordAutofillAgentTest,
PasswordGenerationTriggered_GeneratedPassword) {
SimulateOnFillPasswordForm(fill_data_);
SetFoundFormEligibleForGeneration(
password_generation_, GetMainFrame()->GetDocument(),
"password", nullptr);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
std::u16string password = u"NewPass22";
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, Eq(password)));
password_generation_->GeneratedPasswordAccepted(password);
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), kAliceUsername16, u"NewPass22");
}
TEST_F(PasswordAutofillAgentTest,
ResetPasswordGenerationWhenFieldIsAutofilled) {
SetFoundFormEligibleForGeneration(
password_generation_, GetMainFrame()->GetDocument(),
"password", nullptr);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
std::u16string password = u"NewPass22";
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, Eq(password)));
password_generation_->GeneratedPasswordAccepted(password);
EXPECT_CALL(fake_pw_client_, PasswordNoLongerGenerated);
SimulateOnFillPasswordForm(fill_data_);
base::RunLoop().RunUntilIdle();
WebDocument document = GetMainFrame()->GetDocument();
WebElement element = document.GetElementById(WebString::FromUTF8("password"));
ASSERT_TRUE(element);
WebInputElement password_element = element.To<WebInputElement>();
EXPECT_FALSE(password_element.ShouldRevealPassword());
EXPECT_FALSE(password_element.IsAutofilled());
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), kAliceUsername16, u"NewPass22");
SimulateSuggestionChoiceOfUsernameAndPassword(username_element_,
kBobUsername16, kBobPassword16);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(password_element.ShouldRevealPassword());
EXPECT_TRUE(password_element.IsAutofilled());
test_api(*autofill_agent_).form_tracker().OnFormNoLongerSubmittable();
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), kBobUsername16, kBobPassword16);
}
TEST_F(PasswordAutofillAgentTest, PasswordGenerationSupersedesAutofill) {
LoadHTML(kSignupFormHTML);
WebDocument document = GetMainFrame()->GetDocument();
WebElement element =
document.GetElementById(WebString::FromUTF8("new_password"));
ASSERT_TRUE(element);
password_element_ = element.To<WebInputElement>();
fill_data_.wait_for_username = true;
fill_data_.preferred_login.username_value.clear();
fill_data_.username_element_renderer_id = FieldRendererId();
UpdateUrlForHTML(kSignupFormHTML);
SimulateOnFillPasswordForm(fill_data_);
SetFoundFormEligibleForGeneration(password_generation_,
GetMainFrame()->GetDocument(),
"new_password",
"confirm_password");
ASSERT_TRUE(SimulateElementClick("new_password"));
EXPECT_CALL(fake_pw_client_, AutomaticGenerationAvailable)
.Times(NumShowSuggestionsCalls());
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&fake_pw_client_);
CheckSuggestionsNotShown();
#if !BUILDFLAG(IS_ANDROID)
EXPECT_CALL(fake_pw_client_, GenerationElementLostFocus())
.Times(testing::AnyNumber());
#endif
}
TEST_F(PasswordAutofillAgentTest, CanShowSuggestionsAfterManualGeneration) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
CheckSuggestions(u"", true);
base::test::TestFuture<const std::optional<
::autofill::password_generation::PasswordGenerationUIData>&>
future_for_waiting;
password_generation_->TriggeredGeneratePassword(
future_for_waiting.GetCallback());
EXPECT_TRUE(future_for_waiting.Wait());
const std::u16string kPassword = u"NewPass24";
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, Eq(kPassword)));
password_generation_->GeneratedPasswordAccepted(kPassword);
ASSERT_EQ(password_element_.Value().Utf16(), kPassword);
password_element_.SetValue(WebString());
password_generation_->TextDidChangeInTextField(password_element_,
{});
ASSERT_TRUE(SimulateElementClick(kPasswordName));
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest, FillSuggestionPasswordChangeForms) {
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
for (const auto& selected_element : {username_element_, password_element_}) {
SimulateElementClick(selected_element);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
ClearUsernameAndPasswordFieldValues();
}
}
TEST_F(PasswordAutofillAgentTest,
SuggestionsOnUsernameFieldOfChangePasswordForm) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
ClearUsernameAndPasswordFieldValues();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest,
SuggestionsOnPasswordFieldOfChangePasswordForm) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
ClearUsernameAndPasswordFieldValues();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(password_element_);
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest, NotAutofillNoUsername) {
fill_data_.preferred_login.username_value.clear();
fill_data_.username_element_renderer_id = autofill::FieldRendererId();
fill_data_.additional_logins.clear();
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest,
AutofillNoUsernameWhenOtherCredentialsStored) {
fill_data_.preferred_login.username_value.clear();
ASSERT_FALSE(fill_data_.additional_logins.empty());
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, NoForm_PromptForAJAXSubmitWithoutNavigation) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
FireAjaxSucceeded();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
FormRendererId(), u"Bob", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
NoForm_PromptForAJAXSubmitWithoutNavigation_2) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
ForceLayoutUpdate();
base::RunLoop().RunUntilIdle();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
FormRendererId(), u"Bob", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest, PromptForAJAXSubmitAfterHidingParentElement) {
LoadHTML(kDivWrappedFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
std::string hide_element =
"var outerDiv = document.getElementById('outer');"
"outerDiv.style = 'display:none';";
ExecuteJavaScriptForTests(hide_element.c_str());
ForceLayoutUpdate();
base::RunLoop().RunUntilIdle();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
GetFormUniqueRendererId("form"), u"Bob", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
PromptForAJAXSubmitAfterDeletingParentElement) {
LoadHTML(kDivWrappedFormHTML);
UpdateUsernameAndPasswordElements();
FormRendererId renderer_id = GetFormUniqueRendererId("form");
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
std::string delete_element =
"var outerDiv = document.getElementById('outer');"
"var innerDiv = document.getElementById('inner');"
"outerDiv.removeChild(innerDiv);";
ExecuteJavaScriptForTests(delete_element.c_str());
base::RunLoop().RunUntilIdle();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
renderer_id, u"Bob", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
NoForm_NoPromptForAJAXSubmitWithoutNavigationAndElementsVisible) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_password_form_submitted());
}
TEST_F(PasswordAutofillAgentTest,
NoForm_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared) {
const char kNoFormHTMLWithHiddenField[] =
"<INPUT type='text' id='username'/>"
"<INPUT type='password' id='password'/>"
"<INPUT type='text' id='captcha' style='display:none'/>";
LoadHTML(kNoFormHTMLWithHiddenField);
UpdateUsernameAndPasswordElements();
WebElement captcha_element = GetMainFrame()->GetDocument().GetElementById(
WebString::FromUTF8("captcha"));
ASSERT_TRUE(captcha_element);
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
std::string show_captcha =
"var captcha = document.getElementById('captcha');"
"captcha.style = 'display:inline';";
ExecuteJavaScriptForTests(show_captcha.c_str());
FireAjaxSucceeded();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_dynamic_form_submission());
EXPECT_FALSE(fake_driver_.called_password_form_submitted());
}
TEST_F(PasswordAutofillAgentTest,
NoForm_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared_2) {
const char kNoFormHTMLWithHiddenField[] =
"<INPUT type='text' id='username'/>"
"<INPUT type='password' id='password'/>"
"<INPUT type='text' id='captcha' style='display:none'/>";
LoadHTML(kNoFormHTMLWithHiddenField);
UpdateUsernameAndPasswordElements();
WebElement captcha_element = GetMainFrame()->GetDocument().GetElementById(
WebString::FromUTF8("captcha"));
ASSERT_TRUE(captcha_element);
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
std::string show_captcha =
"var captcha = document.getElementById('captcha');"
"captcha.style = 'display:inline';";
ExecuteJavaScriptForTests(show_captcha.c_str());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_dynamic_form_submission());
EXPECT_FALSE(fake_driver_.called_password_form_submitted());
}
TEST_F(PasswordAutofillAgentTest,
NoAction_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared) {
const char kHTMLWithHiddenField[] =
"<FORM name='LoginTestForm'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='text' id='captcha' style='display:none'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
LoadHTMLWithUrlOverride(kHTMLWithHiddenField, "https://www.example.com");
UpdateUsernameAndPasswordElements();
WebElement captcha_element = GetMainFrame()->GetDocument().GetElementById(
WebString::FromUTF8("captcha"));
ASSERT_TRUE(captcha_element);
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
captcha_element.SetAttribute("style", "display:inline;");
FireAjaxSucceeded();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_dynamic_form_submission());
EXPECT_FALSE(fake_driver_.called_password_form_submitted());
}
TEST_F(PasswordAutofillAgentTest,
NoAction_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared_2) {
const char kHTMLWithHiddenField[] =
"<FORM name='LoginTestForm'>"
" <INPUT type='text' id='username'/>"
" <INPUT type='password' id='password'/>"
" <INPUT type='text' id='captcha' style='display:none'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
LoadHTMLWithUrlOverride(kHTMLWithHiddenField, "https://www.example.com");
UpdateUsernameAndPasswordElements();
WebElement captcha_element = GetMainFrame()->GetDocument().GetElementById(
WebString::FromUTF8("captcha"));
ASSERT_TRUE(captcha_element);
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
captcha_element.SetAttribute("style", "display:inline;");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(fake_driver_.called_dynamic_form_submission());
EXPECT_FALSE(fake_driver_.called_password_form_submitted());
}
TEST_F(PasswordAutofillAgentTest, DriverIsInformedAboutUnfillableField) {
EXPECT_EQ(FocusedFieldType::kUnknown, fake_driver_.last_focused_field_type());
FocusElement(kPasswordName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillablePasswordField,
fake_driver_.last_focused_field_type());
SetElementReadOnly(username_element_, true);
FocusElement(kUsernameName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kUnfillableElement,
fake_driver_.last_focused_field_type());
}
TEST_F(PasswordAutofillAgentTest, DriverIsInformedAboutFillableFields) {
FocusElement("random_field");
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableNonSearchField,
fake_driver_.last_focused_field_type());
FocusElement(kUsernameName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableNonSearchField,
fake_driver_.last_focused_field_type());
FocusElement(kPasswordName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillablePasswordField,
fake_driver_.last_focused_field_type());
SimulateOnFillPasswordForm(fill_data_);
FocusElement(kUsernameName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableUsernameField,
fake_driver_.last_focused_field_type());
}
TEST_F(PasswordAutofillAgentTest, DriverIsInformedAboutFillableSearchField) {
LoadHTML(kSearchFieldHTML);
FocusElement(kSearchField);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableSearchField,
fake_driver_.last_focused_field_type());
}
TEST_F(PasswordAutofillAgentTest,
DriverInformedAboutWebAuthnIfNotPasswordOrUsername) {
LoadHTML(kWebAutnFieldHTML);
UpdateUrlForHTML(kWebAutnFieldHTML);
UpdateUsernameAndPasswordElements();
FocusElement(kUsernameName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableWebauthnTaggedField,
fake_driver_.last_focused_field_type());
FocusElement(kPasswordName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillablePasswordField,
fake_driver_.last_focused_field_type());
SimulateOnFillPasswordForm(fill_data_);
FocusElement(kUsernameName);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableUsernameField,
fake_driver_.last_focused_field_type());
}
TEST_F(PasswordAutofillAgentTest, DriverIsInformedAboutFillableTextArea) {
LoadHTML(kSocialNetworkPostFormHTML);
FocusElement(kSocialMediaTextArea);
fake_driver_.Flush();
EXPECT_EQ(FocusedFieldType::kFillableTextArea,
fake_driver_.last_focused_field_type());
}
TEST_F(PasswordAutofillAgentTest,
SuggestionsOnFormContainingAmbiguousOrEmptyNames) {
const char kEmpty[] = "";
const char kFormContainsEmptyNamesHTML[] =
"<FORM name='WithoutNameIdForm' action='http://www.bidule.com' >"
" <INPUT type='text' placeholder='username'/>"
" <INPUT type='password' placeholder='Password'/>"
" <INPUT type='submit' />"
"</FORM>";
const char kFormContainsAmbiguousNamesHTML[] =
"<FORM name='AmbiguousNameIdForm' action='http://www.bidule.com' >"
" <INPUT type='text' id='credentials' placeholder='username' />"
" <INPUT type='password' id='credentials' placeholder='Password' />"
" <INPUT type='submit' />"
"</FORM>";
const char kChangePasswordFormContainsEmptyNamesHTML[] =
"<FORM name='ChangePwd' action='http://www.bidule.com' >"
" <INPUT type='text' placeholder='username' />"
" <INPUT type='password' placeholder='Old Password' "
" autocomplete='current-password' />"
" <INPUT type='password' placeholder='New Password' "
" autocomplete='new-password' />"
" <INPUT type='submit' />"
"</FORM>";
const char kChangePasswordFormButNoUsername[] =
"<FORM name='ChangePwdButNoUsername' action='http://www.bidule.com' >"
" <INPUT type='password' placeholder='Old Password' "
" autocomplete='current-password' />"
" <INPUT type='password' placeholder='New Password' "
" autocomplete='new-password' />"
" <INPUT type='submit' />"
"</FORM>";
const char kChangePasswordFormButNoOldPassword[] =
"<FORM name='ChangePwdButNoOldPwd' action='http://www.bidule.com' >"
" <INPUT type='text' placeholder='username' />"
" <INPUT type='password' placeholder='New Password' "
" autocomplete='new-password' />"
" <INPUT type='password' placeholder='Retype Password' "
" autocomplete='new-password' />"
" <INPUT type='submit' />"
"</FORM>";
const char kChangePasswordFormButNoAutocompleteAttribute[] =
"<FORM name='ChangePwdButNoAutocomplete' action='http://www.bidule.com'>"
" <INPUT type='text' placeholder='username' />"
" <INPUT type='password' placeholder='Old Password' />"
" <INPUT type='password' placeholder='New Password' />"
" <INPUT type='submit' />"
"</FORM>";
const struct {
const char* html_form;
bool does_trigger_autocomplete_on_fill;
bool has_fillable_username;
const char* expected_username_suggestions;
const char* expected_password_suggestions;
bool expected_is_username_autofillable;
bool expected_is_password_autofillable;
} test_cases[] = {
{kFormContainsEmptyNamesHTML, true, true, kAliceUsername, kAlicePassword,
true, true},
{kFormContainsAmbiguousNamesHTML, true, true, kAliceUsername,
kAlicePassword, true, true},
{kChangePasswordFormContainsEmptyNamesHTML, true, true, kAliceUsername,
kAlicePassword, true, true},
{kChangePasswordFormButNoUsername, true, false, kEmpty, kAlicePassword,
false, true},
{kChangePasswordFormButNoOldPassword, false, true, kEmpty, kEmpty, false,
false},
{kChangePasswordFormButNoAutocompleteAttribute, true, true,
kAliceUsername, kAlicePassword, true, true},
};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(testing::Message() << "html_form: " << test_case.html_form);
LoadHTML(test_case.html_form);
UpdateUrlForHTML(test_case.html_form);
blink::WebDocument document = GetMainFrame()->GetDocument();
std::vector<WebFormElement> forms = document.GetTopLevelForms();
WebFormElement form_element = forms[0];
std::vector<blink::WebFormControlElement> control_elements =
form_util::GetOwnedAutofillableFormControls(document, form_element);
if (test_case.has_fillable_username) {
username_element_ = control_elements[0].To<WebInputElement>();
password_element_ = control_elements[1].To<WebInputElement>();
} else {
username_element_.Reset();
password_element_ = control_elements[0].To<WebInputElement>();
}
if (test_case.does_trigger_autocomplete_on_fill) {
UpdateRendererIDsInFillData();
fill_data_.additional_logins.clear();
ClearUsernameAndPasswordFieldValues();
SimulateOnFillPasswordForm(fill_data_);
if (test_case.has_fillable_username) {
SimulateSuggestionChoice(username_element_);
} else {
SimulateSuggestionChoice(password_element_);
}
CheckTextFieldsDOMState(test_case.expected_username_suggestions,
test_case.expected_is_username_autofillable,
test_case.expected_password_suggestions,
test_case.expected_is_password_autofillable);
}
}
}
TEST_F(PasswordAutofillAgentTest, RememberChosenUsernamePassword) {
SimulateOnFillPasswordForm(fill_data_);
SimulateSuggestionChoiceOfUsernameAndPassword(username_element_,
kBobUsername16, kBobPassword16);
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), kBobUsername16, kBobPassword16);
}
TEST_F(PasswordAutofillAgentTest, ShowSuggestionForNonUsernameFieldForms) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kTwoNoUsernameFormsHTML);
fill_data_.preferred_login.username_value.clear();
UpdateUrlForHTML(kTwoNoUsernameFormsHTML);
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick("password1"));
CheckSuggestions(std::u16string(), true);
ASSERT_TRUE(SimulateElementClick("password2"));
CheckSuggestions(std::u16string(), true);
}
TEST_F(PasswordAutofillAgentTest,
UsernameChangedAfterPasswordInput_AJAXSucceeded) {
for (auto change_source :
{FieldChangeSource::USER, FieldChangeSource::AUTOFILL_SINGLE_FIELD,
FieldChangeSource::USER_AUTOFILL_SINGLE_FIELD,
FieldChangeSource::AUTOFILL_FORM,
FieldChangeSource::USER_AUTOFILL_FORM}) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
SimulateUsernameFieldChange(change_source);
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
FireAjaxSucceeded();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
FormRendererId(), u"Alice", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
}
TEST_F(PasswordAutofillAgentTest,
UsernameChangedAfterPasswordInput_AJAXSucceeded_2) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
SimulateUsernameTyping("Alice");
FireAjaxSucceeded();
std::string hide_elements =
"var password = document.getElementById('password');"
"password.style = 'display:none';"
"var username = document.getElementById('username');"
"username.style = 'display:none';";
ExecuteJavaScriptForTests(hide_elements.c_str());
ForceLayoutUpdate();
base::RunLoop().RunUntilIdle();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
FormRendererId(), u"Alice", u"mypassword",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
TEST_F(PasswordAutofillAgentTest,
UsernameChangedAfterPasswordInput_FormSubmitted) {
for (auto change_source :
{FieldChangeSource::USER, FieldChangeSource::AUTOFILL_SINGLE_FIELD,
FieldChangeSource::USER_AUTOFILL_SINGLE_FIELD,
FieldChangeSource::AUTOFILL_FORM,
FieldChangeSource::USER_AUTOFILL_FORM}) {
LoadHTML(kFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
SimulateUsernameFieldChange(change_source);
SaveAndSubmitForm();
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), u"Alice", u"mypassword");
}
}
TEST_F(PasswordAutofillAgentTest, SuggestPasswordFieldSignInForm) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
base::RunLoop().RunUntilIdle();
SimulateElementClick(password_element_);
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest, SuggestPasswordWhenUsernameFieldDisabled) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
SimulateClosingKeyboardReplacingSurfaceIfAndroid(kPasswordName);
username_element_.SetValue(WebString::FromUTF16(username1_));
SetElementReadOnly(username_element_, true);
SimulateOnFillPasswordForm(fill_data_);
PasswordSuggestionRequest suggestion_request;
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.WillOnce(testing::SaveArg<0>(&suggestion_request));
base::RunLoop().RunUntilIdle();
SimulateElementClick(password_element_);
fake_driver_.Flush();
const FormData& form = *fake_driver_.form_data_parsed()->begin();
uint64_t username_index = std::distance(
form.fields().begin(),
std::ranges::find(form.fields(),
form_util::GetFieldRendererId(username_element_),
&autofill::FormFieldData::renderer_id));
uint64_t password_index = std::distance(
form.fields().begin(),
std::ranges::find(form.fields(),
form_util::GetFieldRendererId(password_element_),
&autofill::FormFieldData::renderer_id));
EXPECT_EQ(suggestion_request.username_field_index, username_index);
EXPECT_EQ(suggestion_request.password_field_index, password_index);
}
#if !BUILDFLAG(IS_ANDROID)
TEST_F(PasswordAutofillAgentTest, SuggestMultiplePasswordFields) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(SimulateElementClick("password"));
CheckSuggestions(u"", true);
ASSERT_TRUE(SimulateElementClick("newpassword"));
CheckSuggestions(u"", true);
ASSERT_TRUE(SimulateElementClick("confirmpassword"));
CheckSuggestions(u"", true);
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
SimulateElementClick(password_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
ASSERT_TRUE(SimulateElementClick("newpassword"));
ASSERT_TRUE(SimulateElementClick("confirmpassword"));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(SimulateElementClick("password"));
CheckSuggestions(u"", true);
}
#endif
TEST_F(PasswordAutofillAgentTest, ShowAutofillSignaturesFlag) {
const bool kFalseTrue[] = {false, true};
for (bool show_signatures : kFalseTrue) {
if (show_signatures)
EnableShowAutofillSignatures();
std::string dom_with_dom_subtree_modified_listener =
base::StrCat({"<SCRIPT>"
"window.addEventListener('DOMSubtreeModified', () => {});"
"</SCRIPT>",
kFormHTML});
LoadHTML(dom_with_dom_subtree_modified_listener.c_str());
WebDocument document = GetMainFrame()->GetDocument();
WebFormElement form_element =
document.GetElementById(WebString::FromASCII("LoginTestForm"))
.To<WebFormElement>();
ASSERT_TRUE(form_element);
WebString form_signature_attribute = WebString::FromASCII("form_signature");
EXPECT_EQ(form_element.HasAttribute(form_signature_attribute),
show_signatures);
}
}
TEST_F(PasswordAutofillAgentTest,
SameDocumentNavigationSubmissionUsernameIsEmpty) {
username_element_.SetValue(WebString());
SimulatePasswordTyping("random");
FormRendererId renderer_id = GetFormUniqueRendererId("LoginTestForm");
ExecuteJavaScriptForTests(kJavaScriptRemoveForm);
FireDidFinishSameDocumentNavigation();
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
renderer_id, std::u16string(), u"random",
SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION);
}
#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
TEST_F(PasswordAutofillAgentTest,
CheckSafeBrowsingReputationWhenUserStartsFillingUsernamePassword) {
ASSERT_EQ(0, fake_driver_.called_check_safe_browsing_reputation_cnt());
ASSERT_TRUE(SimulateElementClick(kPasswordName));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
SimulatePasswordTyping("modify");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
ASSERT_TRUE(SimulateElementClick(kUsernameName));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
ASSERT_TRUE(SimulateElementClick(kPasswordName));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_check_safe_browsing_reputation_cnt());
LoadHTML(kFormHTML);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, fake_driver_.called_check_safe_browsing_reputation_cnt());
}
#endif
TEST_F(PasswordAutofillAgentTest, AutocompleteWhenPageUrlIsChanged) {
fill_data_.url = GURL(fill_data_.url.possibly_invalid_spec() + "/path");
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, NoForm_MultipleAJAXEventsWithoutSubmission) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
FireAjaxSucceeded();
base::RunLoop().RunUntilIdle();
FireAjaxSucceeded();
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_password_form_submitted());
ASSERT_FALSE(static_cast<bool>(fake_driver_.form_data_submitted()));
}
TEST_F(PasswordAutofillAgentTest, ManualFallbackForSaving) {
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword).Times(0);
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
SimulatePasswordTyping(kPasswordName);
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
EXPECT_EQ(3, fake_driver_.called_inform_about_user_input_count());
SetFocused(username_element_);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(4, fake_driver_.called_inform_about_user_input_count());
SimulateUsernameTyping("");
EXPECT_EQ(5, fake_driver_.called_inform_about_user_input_count());
SetFocused(password_element_);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(6, fake_driver_.called_inform_about_user_input_count());
SimulatePasswordTyping("");
EXPECT_EQ(7, fake_driver_.called_inform_about_user_input_count());
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(8, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, ManualFallbackForSaving_PasswordChangeForm) {
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
SimulatePasswordTyping(kPasswordName);
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
WebInputElement new_password = GetInputElementByID("newpassword");
ASSERT_TRUE(new_password);
SetFocused(new_password);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(3, fake_driver_.called_inform_about_user_input_count());
WebInputElement confirmation_password =
GetInputElementByID("confirmpassword");
ASSERT_TRUE(confirmation_password);
SetFocused(confirmation_password);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(4, fake_driver_.called_inform_about_user_input_count());
SimulatePasswordTyping("");
SimulateUserInputChangeForElement(new_password, "");
SimulateUserInputChangeForElement(confirmation_password, "");
EXPECT_EQ(5, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, GaiaReauthenticationFormIgnored) {
fake_driver_.reset_password_forms_calls();
const char kGaiaReauthenticationFormHTML[] =
"<FORM id='ReauthenticationForm'>"
" <INPUT type='hidden' name='continue' "
"value='https://passwords.google.com/'>"
" <INPUT type='hidden' name='rart'>"
" <INPUT type='password' id='password'/>"
" <INPUT type='submit' value='Login'/>"
"</FORM>";
LoadHTMLWithUrlOverride(kGaiaReauthenticationFormHTML,
"https://accounts.google.com");
UpdateOnlyPasswordElement();
SimulateElementClick(password_element_);
fake_driver_.Flush();
ASSERT_TRUE(fake_driver_.called_password_forms_parsed());
const std::vector<autofill::FormData>& parsed_form_data =
fake_driver_.form_data_parsed().value();
ASSERT_EQ(1u, parsed_form_data.size());
EXPECT_TRUE(parsed_form_data[0].is_gaia_with_skip_save_password_form());
}
TEST_F(PasswordAutofillAgentTest,
UpdateSuggestionsIfNewerCredentialsAreSupplied) {
password_autofill_agent_->ApplyFillDataOnParsingCompletion(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
fill_data_.preferred_login.password_value = u"a-changed-password";
password_autofill_agent_->ApplyFillDataOnParsingCompletion(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, "a-changed-password",
true);
}
TEST_F(PasswordAutofillAgentTest, SuggestLatestCredentials) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
password_autofill_agent_->ApplyFillDataOnParsingCompletion(fill_data_);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
base::RunLoop().RunUntilIdle();
fill_data_.preferred_login.username_value = u"a-changed-username";
password_autofill_agent_->ApplyFillDataOnParsingCompletion(fill_data_);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest, PSLMatchedPasswordIsNotAutofill) {
const char kFormWithPrefilledUsernameHTML[] =
"<FORM id='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username' value='prefilledusername'/>"
" <INPUT type='password' id='password'/>"
"</FORM>";
LoadHTML(kFormWithPrefilledUsernameHTML);
UpdateUsernameAndPasswordElements();
UpdateUrlForHTML(kFormWithPrefilledUsernameHTML);
PasswordAndMetadata psl_credentials;
psl_credentials.password_value = u"pslpassword";
psl_credentials.realm = "example.com";
psl_credentials.username_value = u"prefilledusername";
fill_data_.additional_logins.push_back(std::move(psl_credentials));
SimulateOnFillPasswordForm(fill_data_);
CheckUsernameDOMStatePasswordSuggestedState("prefilledusername", false, "",
false);
}
TEST_F(PasswordAutofillAgentTest, FillOnLoadWith) {
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
}
TEST_F(PasswordAutofillAgentTest, FillOnLoadNoForm) {
LoadHTML(kNoFormHTML);
UpdateUsernameAndPasswordElements();
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, FillOnLoadNoUsername) {
LoadHTML(kTwoNoUsernameFormsHTML);
username_element_.Reset();
fill_data_.preferred_login.username_value.clear();
password_element_ = GetInputElementByID("password2");
UpdateRendererIDsInFillData();
SimulateOnFillPasswordForm(fill_data_);
EXPECT_EQ(kAlicePassword, password_element_.SuggestedValue().Utf8());
}
TEST_F(PasswordAutofillAgentTest, FormToFillIsPrefilled) {
username_element_.SetValue(WebString::FromUTF8("prefilled_placeholder"));
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("prefilled_placeholder",
false, "",
false);
}
TEST_F(PasswordAutofillAgentTest, RestoresAfterJavaScriptModification) {
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
fake_driver_.reset_password_forms_calls();
static const char script[] = "document.getElementById('username').value = ''";
ExecuteJavaScriptForTests(script);
CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
password_autofill_agent_->OnDynamicFormsSeen({});
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
EXPECT_FALSE(fake_driver_.called_password_forms_rendered());
}
TEST_F(PasswordAutofillAgentTest, DoNotRestoreWhenFormStructureWasChanged) {
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
static const char clear_username_script[] =
"document.getElementById('username').value = ''";
ExecuteJavaScriptForTests(clear_username_script);
static const char add_input_element_script[] =
"document.getElementById('LoginTestForm').appendChild(document."
"createElement('input'))";
ExecuteJavaScriptForTests(add_input_element_script);
CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
password_autofill_agent_->OnDynamicFormsSeen({});
CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, FillOnLoadSingleUsername) {
fill_data_.preferred_login.password_value.clear();
fill_data_.password_element_renderer_id = autofill::FieldRendererId();
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, std::string(), false);
CheckTextFieldsDOMState(std::string(), true, std::string(), false);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
CheckTextFieldsDOMState(kAliceUsername, true, std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, SingleUsernamePreviewSuggestion) {
fill_data_.preferred_login.password_value.clear();
fill_data_.password_element_renderer_id = autofill::FieldRendererId();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
password_autofill_agent_->PreviewSuggestion(
username_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsSuggestedState(kAliceUsername, true, std::string(), false);
password_autofill_agent_->PreviewSuggestion(username_element_, kBobUsername16,
kCarolPassword16);
CheckTextFieldsSuggestedState(kBobUsername, true, std::string(), false);
}
TEST_F(PasswordAutofillAgentTest, SingleUsernameFillSuggestion) {
fill_data_.preferred_login.password_value.clear();
fill_data_.password_element_renderer_id = autofill::FieldRendererId();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
SimulateElementClick(username_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState(kAliceUsername, true, std::string(), false);
int username_length = strlen(kAliceUsername);
CheckUsernameSelection(username_length, username_length);
password_autofill_agent_->FillPasswordSuggestion(
kBobUsername16, kCarolPassword16, base::DoNothing());
CheckTextFieldsDOMState(kBobUsername, true, std::string(), false);
username_length = strlen(kBobUsername);
CheckUsernameSelection(username_length, username_length);
}
TEST_F(PasswordAutofillAgentTest, SingleUsernameClearPreview) {
fill_data_.preferred_login.password_value.clear();
fill_data_.password_element_renderer_id = autofill::FieldRendererId();
ResetFieldState(&username_element_, "ali", WebAutofillState::kPreviewed);
ASSERT_TRUE(SimulateElementClick(kUsernameName));
username_element_.SetSelectionRange(0, 0);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsDOMState("ali", true, std::string(), false);
password_autofill_agent_->PreviewSuggestion(
username_element_, kAliceUsername16, kAlicePassword16);
password_autofill_agent_->ClearPreviewedForm();
EXPECT_TRUE(username_element_.SuggestedValue().IsEmpty());
CheckTextFieldsDOMState("ali", true, std::string(), false);
CheckUsernameSelection(0, 0);
}
TEST_F(PasswordAutofillAgentTest, NoUsernameCredential) {
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
const char kPasswordForEmptyUsernameCredential[] = "empty";
const char16_t kPasswordForEmptyUsernameCredential16[] = u"empty";
PasswordAndMetadata empty_username_credential;
empty_username_credential.password_value =
kPasswordForEmptyUsernameCredential16;
empty_username_credential.username_value = u"";
fill_data_.additional_logins.push_back(std::move(empty_username_credential));
SimulateOnFillPasswordForm(fill_data_);
ClearUsernameAndPasswordFieldValues();
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
SimulateSuggestionChoiceOfUsernameAndPassword(
password_element_, kAliceUsername16, kAlicePassword16);
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
base::RunLoop().RunUntilIdle();
EXPECT_CALL(fake_driver_, ShowPasswordSuggestions)
.Times(NumShowSuggestionsCalls());
SimulateSuggestionChoiceOfUsernameAndPassword(
password_element_, u"", kPasswordForEmptyUsernameCredential16);
CheckTextFieldsDOMState(kAliceUsername, true,
kPasswordForEmptyUsernameCredential, true);
}
TEST_F(PasswordAutofillAgentTest, NoRefillOfUserInput) {
ClearUsernameAndPasswordFieldValues();
SimulateOnFillPasswordForm(fill_data_);
ASSERT_TRUE(SimulateElementClick(kPasswordName));
SimulatePasswordTyping("newpwd");
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsStateForElements(username_element_, kAliceUsername, true,
password_element_, "newpwd", false, false,
false);
}
TEST_F(PasswordAutofillAgentTest, XhrSubmissionAfterFillingSuggestion) {
SimulateOnFillPasswordForm(fill_data_);
SimulateSuggestionChoiceOfUsernameAndPassword(username_element_,
kBobUsername16, kBobPassword16);
ExecuteJavaScriptForTests(kJavaScriptRemoveForm);
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
fill_data_.form_renderer_id, kBobUsername16, kBobPassword16,
SubmissionIndicatorEvent::DOM_MUTATION_AFTER_AUTOFILL);
}
TEST_F(PasswordAutofillAgentTest, NoXhrSubmissionAfterFillingOnPageload) {
SimulateOnFillPasswordForm(fill_data_);
ExecuteJavaScriptForTests(kJavaScriptRemoveForm);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_dynamic_form_submission());
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordField) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
EXPECT_CALL(fake_driver_,
UserModifiedNonPasswordField(
form_util::GetFieldRendererId(username_element_),
std::u16string(kAliceUsername16),
true,
false));
SimulateUsernameTyping(kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldOneSymbol) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
EXPECT_CALL(fake_driver_, UserModifiedNonPasswordField).Times(0);
SimulateUsernameTyping("1");
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldTooManySymbols) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
EXPECT_CALL(fake_driver_, UserModifiedNonPasswordField).Times(0);
std::string not_username(101, 'a');
SimulateUsernameTyping(not_username);
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldOTPAutocomplete) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
username_element_.SetAttribute(
"autocomplete",
password_manager::constants::kAutocompleteOneTimePassword);
EXPECT_CALL(fake_driver_,
UserModifiedNonPasswordField(
form_util::GetFieldRendererId(username_element_),
std::u16string(kAliceUsername16),
false,
true));
SimulateUsernameTyping(kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldOTPName) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
username_element_.SetAttribute("name", "test-one-time-pass");
EXPECT_CALL(fake_driver_,
UserModifiedNonPasswordField(
form_util::GetFieldRendererId(username_element_),
std::u16string(kAliceUsername16),
true,
true));
SimulateUsernameTyping(kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, ModifyNonPasswordFieldShortName) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
username_element_.SetAttribute("name", "i");
username_element_.SetAttribute("id", "i");
ASSERT_TRUE(username_element_.NameForAutofill().length() == 1);
#if BUILDFLAG(IS_ANDROID)
FocusFirstInputElement();
#endif
EXPECT_CALL(fake_driver_, UserModifiedNonPasswordField).Times(0);
SimulateUserInputChangeForElement(username_element_, kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, ModifySearchField) {
LoadHTML(kSingleUsernameFormHTML);
UpdateOnlyUsernameElement();
username_element_.SetAttribute("name", "thesearchfield");
#if BUILDFLAG(IS_ANDROID)
FocusFirstInputElement();
#endif
EXPECT_CALL(fake_driver_, UserModifiedNonPasswordField).Times(0);
SimulateUserInputChangeForElement(username_element_, kAliceUsername);
}
TEST_F(PasswordAutofillAgentTest, ModifyFieldsByManualFillingNotifiesBrowser) {
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
EXPECT_CALL(fake_driver_,
UserModifiedNonPasswordField(
form_util::GetFieldRendererId(username_element_),
std::u16string(kAliceUsername16),
false,
false));
EXPECT_CALL(fake_driver_, UserModifiedPasswordField);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
}
TEST_F(PasswordAutofillAgentTest,
ModifyFieldsByFillingOnPageloadDoesNotNotifyBrowser) {
EXPECT_CALL(fake_driver_, UserModifiedNonPasswordField).Times(0);
EXPECT_CALL(fake_driver_, UserModifiedPasswordField).Times(0);
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
CheckTextFieldsDOMState(kAliceUsername, true,
kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest,
ProvisionalPasswordSavingWhenFormTagHostsShadowDom) {
LoadHTML(kFormTagHostsShadowDomInputs);
username_element_ =
GetElementByID("un_host").ShadowRoot().FirstChild().To<WebInputElement>();
ASSERT_TRUE(username_element_);
password_element_ =
GetElementByID("pw_host").ShadowRoot().FirstChild().To<WebInputElement>();
ASSERT_TRUE(password_element_);
username_element_.SetValue(WebString::FromUTF8(kAliceUsername));
password_autofill_agent_->UpdatePasswordStateForTextChange(username_element_,
{});
fake_driver_.Flush();
EXPECT_EQ(fake_driver_.called_inform_about_user_input_count(), 1);
password_element_.SetValue(WebString::FromUTF8(kAlicePassword));
password_autofill_agent_->UpdatePasswordStateForTextChange(password_element_,
{});
fake_driver_.Flush();
EXPECT_EQ(fake_driver_.called_inform_about_user_input_count(), 2);
ASSERT_TRUE(fake_driver_.form_data_maybe_submitted().has_value());
FormData submitted_form = fake_driver_.form_data_maybe_submitted().value();
EXPECT_EQ(submitted_form.name(), u"shadyform");
EXPECT_TRUE(FormHasFieldWithValue(submitted_form, kAliceUsername16));
EXPECT_TRUE(FormHasFieldWithValue(submitted_form, kAlicePassword16));
}
TEST_F(PasswordAutofillAgentTest,
PasswordSuggestionFillingWhenFormTagHostsShadowDom) {
LoadHTML(kFormTagHostsShadowDomInputs);
ASSERT_TRUE(UpdateFormElementsForFormHostingShadowDom());
UpdateRendererIDsInFillData();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(password_element_);
CheckTextFieldsDOMState(std::string(), false, std::string(), false);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
}
TEST_F(PasswordAutofillAgentTest, PasswordGenerationWhenFormTagHostsShadowDom) {
LoadHTML(kFormTagHostsShadowDomInputs);
ASSERT_TRUE(UpdateFormElementsForFormHostingShadowDom());
UpdateRendererIDsInFillData();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(password_element_);
base::test::TestFuture<const std::optional<
::autofill::password_generation::PasswordGenerationUIData>&>
future_for_waiting;
password_generation_->TriggeredGeneratePassword(
future_for_waiting.GetCallback());
EXPECT_TRUE(future_for_waiting.Wait());
const std::u16string kPassword = u"GeneratedPass24";
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, Eq(kPassword)));
password_generation_->GeneratedPasswordAccepted(kPassword);
EXPECT_EQ(password_element_.Value().Utf16(), kPassword);
}
TEST_F(PasswordAutofillAgentTest, JSFieldModificationPasswordForm) {
ASSERT_EQ(fake_driver_.called_inform_about_user_input_count(), 0);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
const std::string kJsUsername = "js-set-username";
const std::string kJsPassword = "js-set-password";
ExecuteJavaScriptForTests(R"(document.getElementById('username').value = ')" +
kJsUsername + R"(';
document.getElementById('password').value = ')" +
kJsPassword + "';");
fake_driver_.Flush();
EXPECT_EQ(fake_driver_.called_inform_about_user_input_count(), 2);
ASSERT_TRUE(fake_driver_.form_data_maybe_submitted().has_value());
FormData form_data = fake_driver_.form_data_maybe_submitted().value();
ASSERT_EQ(form_data.fields().size(), 3u);
EXPECT_EQ(form_data.fields()[1].value(), base::ASCIIToUTF16(kJsUsername));
EXPECT_EQ(form_data.fields()[2].value(), base::ASCIIToUTF16(kJsPassword));
}
TEST_F(PasswordAutofillAgentTest, JSFieldModificationUnrelatedField) {
ASSERT_EQ(fake_driver_.called_inform_about_user_input_count(), 0);
ExecuteJavaScriptForTests(
R"(document.getElementById('random_field').value = 'js-set-whatever';)");
fake_driver_.Flush();
EXPECT_EQ(fake_driver_.called_inform_about_user_input_count(), 0);
}
TEST_F(PasswordAutofillAgentTest, JSFieldModificationNonTextInput) {
ASSERT_EQ(fake_driver_.called_inform_about_user_input_count(), 0);
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
ExecuteJavaScriptForTests(
R"(document.getElementById('username').type = 'hidden';)");
ExecuteJavaScriptForTests(
R"(document.getElementById('username').value = 'js-set-whatever';)");
fake_driver_.Flush();
EXPECT_EQ(fake_driver_.called_inform_about_user_input_count(), 0);
}
TEST_F(PasswordAutofillAgentTest, TimesReceivedFillDataForFormMetric) {
SimulateOnFillPasswordForm(fill_data_);
fill_data_.username_element_renderer_id = FieldRendererId();
SimulateOnFillPasswordForm(fill_data_);
fill_data_.username_element_renderer_id = FieldRendererId(404);
fill_data_.password_element_renderer_id = FieldRendererId(40404);
SimulateOnFillPasswordForm(fill_data_);
password_autofill_agent_->ReadyToCommitNavigation(nullptr);
histogram_tester_.ExpectUniqueSample(
"PasswordManager.TimesReceivedFillDataForForm", 2, 1);
}
TEST_F(PasswordAutofillAgentTest,
ShowSuggestionsOnParsingAutofocusedPasswordForm) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kShowSuggestionsOnAutofocus);
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
#if BUILDFLAG(IS_ANDROID)
BlurElement(kUsernameName);
#endif
FocusElement(kUsernameName);
CheckSuggestionsNotShown();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
#if BUILDFLAG(IS_ANDROID)
CheckSuggestionsNotShown();
#else
CheckSuggestions(u"", true);
#endif
}
TEST_F(PasswordAutofillAgentTest,
ShowSuggestionsOnParsingAutofocusedWebAuthnForm) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kShowSuggestionsOnAutofocus);
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
LoadHTML(kWebAutnFieldHTML);
UpdateUsernameAndPasswordElements();
UpdateRendererIDsInFillData();
#if BUILDFLAG(IS_ANDROID)
BlurElement(kUsernameName);
#endif
FocusElement(kUsernameName);
CheckSuggestionsNotShown();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckSuggestions(u"", true);
}
TEST_F(PasswordAutofillAgentTest,
DoNotShowSuggestionsOnParsingFocusedFormSecondTime) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kShowSuggestionsOnAutofocus);
base::test::ScopedFeatureList test_feature_list;
test_feature_list.InitAndDisableFeature(
features::kAutofillAndPasswordsInSameSurface);
#if BUILDFLAG(IS_ANDROID)
BlurElement(kUsernameName);
#endif
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(kUsernameName);
CheckSuggestions(u"", true);
fill_data_.preferred_login.username_value = u"new_username";
SimulateOnFillPasswordForm(fill_data_);
CheckSuggestionsNotShown();
}
TEST_F(PasswordAutofillAgentTest,
DoNotShowSuggestionsOnParsingFormWithoutFocus) {
scoped_feature_list_.InitAndEnableFeature(
password_manager::features::kShowSuggestionsOnAutofocus);
#if BUILDFLAG(IS_ANDROID)
BlurElement(kUsernameName);
#endif
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
CheckSuggestionsNotShown();
}
TEST_F(PasswordAutofillAgentTest, InformingBrowserAboutUsernameTextFields) {
LoadHTML(kSingleTextInputFormHTML);
UpdateOnlyUsernameElement();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, InformingBrowserAboutIrrelevantTextFields) {
LoadHTML(kSingleTextInputFormHTML);
UpdateOnlyUsernameElement();
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, NoFillingFallbackForBannedFields) {
LoadHTML(
R"(
<input type="text" id="username-field" name="username-field">
<input type="password" id="password-field" name="password-field">
<input type="text" id="credit-card-full-name"
name="credit-card-full-name" placeholder="Full Name">
<input type="password" id="credit-card-number"
name="credit-card-number" placeholder="Card number">
<input type="password" id="credit-card-cvc" name="credit-card-cvc"
placeholder="CVC">
)");
WebInputElement username_field = GetInputElementByID("username-field");
WebInputElement password_field = GetInputElementByID("username-field");
WebInputElement credit_card_full_name_field =
GetInputElementByID("credit-card-full-name");
WebInputElement credit_card_number_field =
GetInputElementByID("credit-card-number");
WebInputElement credit_card_cvc_field =
GetInputElementByID("credit-card-cvc");
PasswordFormFillData form_data;
form_data.form_renderer_id = FormRendererId();
form_data.username_element_renderer_id = FieldRef(username_field).GetId();
form_data.password_element_renderer_id = FieldRef(password_field).GetId();
form_data.preferred_login.username_value = kAliceUsername16;
form_data.preferred_login.password_value = kAlicePassword16;
form_data.suggestion_banned_fields = {
FieldRef(credit_card_full_name_field).GetId(),
FieldRef(credit_card_number_field).GetId(),
FieldRef(credit_card_cvc_field).GetId()};
password_autofill_agent_->ApplyFillDataOnParsingCompletion(form_data);
EXPECT_TRUE(
password_autofill_agent_
->CreateRequestForDomain(
username_field,
AutofillSuggestionTriggerSource::kFormControlElementClicked,
{})
.has_value());
EXPECT_TRUE(
password_autofill_agent_
->CreateRequestForDomain(
password_field,
AutofillSuggestionTriggerSource::kFormControlElementClicked,
{})
.has_value());
EXPECT_FALSE(
password_autofill_agent_
->CreateRequestForDomain(
credit_card_full_name_field,
AutofillSuggestionTriggerSource::kFormControlElementClicked,
{})
.has_value());
EXPECT_FALSE(
password_autofill_agent_
->CreateRequestForDomain(
credit_card_number_field,
AutofillSuggestionTriggerSource::kFormControlElementClicked,
{})
.has_value());
EXPECT_FALSE(
password_autofill_agent_
->CreateRequestForDomain(
credit_card_cvc_field,
AutofillSuggestionTriggerSource::kFormControlElementClicked,
{})
.has_value());
}
TEST_F(PasswordAutofillAgentTest, FillChangePasswordForm) {
std::vector<std::string> htms_to_test = {kPasswordChangeFormHTML,
kPasswordChangeWithoutFormHTML};
for (const std::string& html : htms_to_test) {
SCOPED_TRACE(testing::Message() << "Running for the page: " << html);
LoadHTML(html);
UpdateUrlForHTML(html);
WebInputElement password = GetInputElementByID("password"),
new_password = GetInputElementByID("newpassword"),
confirmation_password =
GetInputElementByID("confirmpassword");
auto password_id = autofill::form_util::GetFieldRendererId(password),
new_password_id =
autofill::form_util::GetFieldRendererId(new_password),
password_confirmation =
autofill::form_util::GetFieldRendererId(confirmation_password);
const std::vector<autofill::FormData>& parsed_form_data =
fake_driver_.form_data_parsed().value();
EXPECT_EQ(1u, parsed_form_data.size());
base::MockCallback<
base::OnceCallback<void(const std::optional<autofill::FormData>&)>>
mock_reply;
EXPECT_CALL(mock_reply, Run(Optional(ResultOf(
[](FormData form) {
return test::WithoutUnserializedData(
test::WithoutValues(std::move(form)));
},
parsed_form_data[0]))));
password_autofill_agent_->FillChangePasswordForm(
password_id, new_password_id, password_confirmation, u"qwerty",
u"Pa$sw0rD", mock_reply.Get());
EXPECT_EQ(u"qwerty", password.Value().Utf16());
EXPECT_EQ(u"Pa$sw0rD", new_password.Value().Utf16());
EXPECT_EQ(u"Pa$sw0rD", confirmation_password.Value().Utf16());
}
}
TEST_F(PasswordAutofillAgentTest, FillChangePasswordFormFailed) {
LoadHTML(kPasswordChangeFormHTML);
UpdateUrlForHTML(kPasswordChangeFormHTML);
WebInputElement password = GetInputElementByID("password"),
new_password = GetInputElementByID("newpassword"),
confirmation_password =
GetInputElementByID("confirmpassword");
base::MockCallback<
base::OnceCallback<void(const std::optional<autofill::FormData>&)>>
mock_reply;
EXPECT_CALL(mock_reply, Run(Eq(std::nullopt)));
password_autofill_agent_->FillChangePasswordForm(
autofill::FieldRendererId(0), autofill::FieldRendererId(0),
autofill::FieldRendererId(0), u"qwerty", u"Pa$sw0rD", mock_reply.Get());
EXPECT_EQ(u"", password.Value().Utf16());
EXPECT_EQ(u"", new_password.Value().Utf16());
EXPECT_EQ(u"", confirmation_password.Value().Utf16());
}
TEST_F(PasswordAutofillAgentTest,
DynamicFormSubmissionDetectedAfterFillingOnPageLoad) {
LoadHTML(kDivWrappedFormHTML);
UpdateUsernameAndPasswordElements();
FormRendererId renderer_id = GetFormUniqueRendererId("form");
fill_data_.notify_browser_of_successful_filling = true;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
FireAjaxSucceeded();
constexpr char kDeleteElement[] = "document.getElementById('inner').remove()";
ExecuteJavaScriptForTests(kDeleteElement);
ExpectDynamicFormSubmissionWithUsernameAndPasswords(
renderer_id, kAliceUsername16, kAlicePassword16,
SubmissionIndicatorEvent::DOM_MUTATION_AFTER_AUTOFILL);
}
TEST_F(PasswordAutofillAgentTest, DynamicFormSubmissionNotDetected) {
LoadHTML(kDivWrappedFormHTML);
UpdateUsernameAndPasswordElements();
fill_data_.notify_browser_of_successful_filling = false;
SimulateOnFillPasswordForm(fill_data_);
CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true);
CheckFirstFillingResult(FillingResult::kSuccess);
FireAjaxSucceeded();
constexpr char kDeleteElement[] = "document.getElementById('inner').remove()";
ExecuteJavaScriptForTests(kDeleteElement);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_dynamic_form_submission());
ASSERT_FALSE(fake_driver_.form_data_maybe_submitted());
}
#if BUILDFLAG(IS_ANDROID)
TEST_F(PasswordAutofillAgentTest, TriggerFormSubmission_HiddenPasswordField) {
const char kUsernameFirstFormHTML[] =
"<script>"
" function on_keypress(event) {"
" if (event.which === 13) {"
" var field = document.getElementById('password');"
" field.parentElement.removeChild(field);"
" }"
" }"
"</script>"
"<INPUT type='text' id='username' onkeypress='on_keypress(event)'/>"
"<INPUT type='password' id='password' style='display:none'/>";
LoadHTML(kUsernameFirstFormHTML);
base::RunLoop().RunUntilIdle();
UpdateUsernameAndPasswordElements();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
base::RunLoop().RunUntilIdle();
password_autofill_agent_->TriggerFormSubmission();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(fake_driver_.called_dynamic_form_submission());
}
class PasswordAutofillAgentFormPresenceVariationTest
: public PasswordAutofillAgentTest,
public testing::WithParamInterface<bool> {};
TEST_P(PasswordAutofillAgentFormPresenceVariationTest, TriggerFormSubmission) {
bool has_form_tag = GetParam();
LoadHTML(has_form_tag ? kFormHTML : kNoFormHTML);
base::RunLoop().RunUntilIdle();
UpdateUsernameAndPasswordElements();
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
SimulateElementClick(username_element_);
password_autofill_agent_->FillPasswordSuggestion(
kAliceUsername16, kAlicePassword16, base::DoNothing());
base::RunLoop().RunUntilIdle();
password_autofill_agent_->TriggerFormSubmission();
base::RunLoop().RunUntilIdle();
if (has_form_tag)
EXPECT_TRUE(fake_driver_.called_password_form_submitted());
else
EXPECT_TRUE(fake_driver_.called_dynamic_form_submission());
fake_driver_.reset_password_forms_calls();
}
INSTANTIATE_TEST_SUITE_P(FormPresenceVariation,
PasswordAutofillAgentFormPresenceVariationTest,
testing::Bool());
#endif
}
}