1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/test/histogram_tester.h"
10 #include "chrome/renderer/autofill/password_generation_test_utils.h"
11 #include "chrome/test/base/chrome_render_view_test.h"
12 #include "components/autofill/content/common/autofill_messages.h"
13 #include "components/autofill/content/renderer/autofill_agent.h"
14 #include "components/autofill/content/renderer/test_password_generation_agent.h"
15 #include "components/autofill/core/common/form_data.h"
16 #include "components/autofill/core/common/password_generation_util.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/WebKit/public/platform/WebString.h"
19 #include "third_party/WebKit/public/web/WebDocument.h"
20 #include "third_party/WebKit/public/web/WebLocalFrame.h"
21 #include "third_party/WebKit/public/web/WebWidget.h"
22 #include "ui/events/keycodes/keyboard_codes.h"
24 using blink::WebDocument
;
25 using blink::WebElement
;
26 using blink::WebInputElement
;
28 using blink::WebString
;
32 class PasswordGenerationAgentTest
: public ChromeRenderViewTest
{
34 PasswordGenerationAgentTest() {}
36 void TearDown() override
{
38 ChromeRenderViewTest::TearDown();
41 void LoadHTMLWithUserGesture(const char* html
) {
44 // Enable show-ime event when element is focused by indicating that a user
45 // gesture has been processed since load.
46 EXPECT_TRUE(SimulateElementClick("dummy"));
49 void FocusField(const char* element_id
) {
50 WebDocument document
= GetMainFrame()->document();
51 blink::WebElement element
=
52 document
.getElementById(blink::WebString::fromUTF8(element_id
));
53 ASSERT_FALSE(element
.isNull());
54 ExecuteJavaScriptForTests(
55 base::StringPrintf("document.getElementById('%s').focus();",
59 void ExpectGenerationAvailable(const char* element_id
,
61 FocusField(element_id
);
62 const IPC::Message
* message
=
63 render_thread_
->sink().GetFirstMessageMatching(
64 AutofillHostMsg_ShowPasswordGenerationPopup::ID
);
68 ASSERT_FALSE(message
);
70 render_thread_
->sink().ClearMessages();
74 DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest
);
77 const char kSigninFormHTML
[] =
78 "<FORM name = 'blah' action = 'http://www.random.com/'> "
79 " <INPUT type = 'text' id = 'username'/> "
80 " <INPUT type = 'password' id = 'password'/> "
81 " <INPUT type = 'button' id = 'dummy'/> "
82 " <INPUT type = 'submit' value = 'LOGIN' />"
85 const char kAccountCreationFormHTML
[] =
86 "<FORM name = 'blah' action = 'http://www.random.com/'> "
87 " <INPUT type = 'text' id = 'username'/> "
88 " <INPUT type = 'password' id = 'first_password' "
89 " autocomplete = 'off' size = 5/>"
90 " <INPUT type = 'password' id = 'second_password' size = 5/> "
91 " <INPUT type = 'text' id = 'address'/> "
92 " <INPUT type = 'button' id = 'dummy'/> "
93 " <INPUT type = 'submit' value = 'LOGIN' />"
96 const char kDisabledElementAccountCreationFormHTML
[] =
97 "<FORM name = 'blah' action = 'http://www.random.com/'> "
98 " <INPUT type = 'text' id = 'username'/> "
99 " <INPUT type = 'password' id = 'first_password' "
100 " autocomplete = 'off' size = 5/>"
101 " <INPUT type = 'password' id = 'second_password' size = 5/> "
102 " <INPUT type = 'text' id = 'address'/> "
103 " <INPUT type = 'text' id = 'disabled' disabled/> "
104 " <INPUT type = 'button' id = 'dummy'/> "
105 " <INPUT type = 'submit' value = 'LOGIN' />"
108 const char kHiddenPasswordAccountCreationFormHTML
[] =
109 "<FORM name = 'blah' action = 'http://www.random.com/'> "
110 " <INPUT type = 'text' id = 'username'/> "
111 " <INPUT type = 'password' id = 'first_password'/> "
112 " <INPUT type = 'password' id = 'second_password' style='display:none'/> "
113 " <INPUT type = 'button' id = 'dummy'/> "
114 " <INPUT type = 'submit' value = 'LOGIN' />"
117 const char kInvalidActionAccountCreationFormHTML
[] =
118 "<FORM name = 'blah' action = 'invalid'> "
119 " <INPUT type = 'text' id = 'username'/> "
120 " <INPUT type = 'password' id = 'first_password'/> "
121 " <INPUT type = 'password' id = 'second_password'/> "
122 " <INPUT type = 'button' id = 'dummy'/> "
123 " <INPUT type = 'submit' value = 'LOGIN' />"
126 const char kMultipleAccountCreationFormHTML
[] =
127 "<FORM name = 'login' action = 'http://www.random.com/'> "
128 " <INPUT type = 'text' id = 'random'/> "
129 " <INPUT type = 'text' id = 'username'/> "
130 " <INPUT type = 'password' id = 'password'/> "
131 " <INPUT type = 'button' id = 'dummy'/> "
132 " <INPUT type = 'submit' value = 'LOGIN' />"
134 "<FORM name = 'signup' action = 'http://www.random.com/signup'> "
135 " <INPUT type = 'text' id = 'username'/> "
136 " <INPUT type = 'password' id = 'first_password' "
137 " autocomplete = 'off' size = 5/>"
138 " <INPUT type = 'password' id = 'second_password' size = 5/> "
139 " <INPUT type = 'text' id = 'address'/> "
140 " <INPUT type = 'submit' value = 'LOGIN' />"
143 const char kBothAutocompleteAttributesFormHTML
[] =
144 "<FORM name = 'blah' action = 'http://www.random.com/'> "
145 " <INPUT type = 'text' autocomplete='username' id = 'username'/> "
146 " <INPUT type = 'password' id = 'first_password' "
147 " autocomplete = 'new-password' size = 5/>"
148 " <INPUT type = 'password' id = 'second_password' size = 5/> "
149 " <INPUT type = 'button' id = 'dummy'/> "
150 " <INPUT type = 'submit' value = 'LOGIN' />"
153 const char kUsernameAutocompleteAttributeFormHTML
[] =
154 "<FORM name = 'blah' action = 'http://www.random.com/'> "
155 " <INPUT type = 'text' autocomplete='username' id = 'username'/> "
156 " <INPUT type = 'password' id = 'first_password' size = 5/>"
157 " <INPUT type = 'password' id = 'second_password' size = 5/> "
158 " <INPUT type = 'button' id = 'dummy'/> "
159 " <INPUT type = 'submit' value = 'LOGIN' />"
162 const char kNewPasswordAutocompleteAttributeFormHTML
[] =
163 "<FORM name = 'blah' action = 'http://www.random.com/'> "
164 " <INPUT type = 'text' id = 'username'/> "
165 " <INPUT type = 'password' id = 'first_password' "
166 " autocomplete='new-password' size = 5/>"
167 " <INPUT type = 'password' id = 'second_password' size = 5/> "
168 " <INPUT type = 'button' id = 'dummy'/> "
169 " <INPUT type = 'submit' value = 'LOGIN' />"
172 const char ChangeDetectionScript
[] =
174 " firstOnChangeCalled = false;"
175 " secondOnChangeCalled = false;"
176 " document.getElementById('first_password').onchange = function() {"
177 " firstOnChangeCalled = true;"
179 " document.getElementById('second_password').onchange = function() {"
180 " secondOnChangeCalled = true;"
184 TEST_F(PasswordGenerationAgentTest
, DetectionTest
) {
185 // Don't shown the icon for non account creation forms.
186 LoadHTMLWithUserGesture(kSigninFormHTML
);
187 ExpectGenerationAvailable("password", false);
189 // We don't show the decoration yet because the feature isn't enabled.
190 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
191 ExpectGenerationAvailable("first_password", false);
193 // Pretend like We have received message indicating site is not blacklisted,
194 // and we have received message indicating the form is classified as
195 // ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
196 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
197 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
198 SetAccountCreationFormsDetectedMessage(password_generation_
,
199 GetMainFrame()->document(),
201 ExpectGenerationAvailable("first_password", true);
203 // Hidden fields are not treated differently.
204 LoadHTMLWithUserGesture(kHiddenPasswordAccountCreationFormHTML
);
205 SetNotBlacklistedMessage(password_generation_
,
206 kHiddenPasswordAccountCreationFormHTML
);
207 SetAccountCreationFormsDetectedMessage(password_generation_
,
208 GetMainFrame()->document(),
210 ExpectGenerationAvailable("first_password", true);
212 // This doesn't trigger because the form action is invalid.
213 LoadHTMLWithUserGesture(kInvalidActionAccountCreationFormHTML
);
214 SetNotBlacklistedMessage(password_generation_
,
215 kInvalidActionAccountCreationFormHTML
);
216 SetAccountCreationFormsDetectedMessage(password_generation_
,
217 GetMainFrame()->document(),
219 ExpectGenerationAvailable("first_password", false);
222 TEST_F(PasswordGenerationAgentTest
, FillTest
) {
223 // Make sure that we are enabled before loading HTML.
224 std::string html
= std::string(kAccountCreationFormHTML
) +
225 ChangeDetectionScript
;
226 LoadHTMLWithUserGesture(html
.c_str());
227 SetNotBlacklistedMessage(password_generation_
, html
.c_str());
228 SetAccountCreationFormsDetectedMessage(password_generation_
,
229 GetMainFrame()->document(),
232 WebDocument document
= GetMainFrame()->document();
234 document
.getElementById(WebString::fromUTF8("first_password"));
235 ASSERT_FALSE(element
.isNull());
236 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
237 element
= document
.getElementById(WebString::fromUTF8("second_password"));
238 ASSERT_FALSE(element
.isNull());
239 WebInputElement second_password_element
= element
.to
<WebInputElement
>();
241 // Both password fields should be empty.
242 EXPECT_TRUE(first_password_element
.value().isNull());
243 EXPECT_TRUE(second_password_element
.value().isNull());
245 base::string16 password
= base::ASCIIToUTF16("random_password");
246 AutofillMsg_GeneratedPasswordAccepted
msg(0, password
);
247 password_generation_
->OnMessageReceived(msg
);
249 // Password fields are filled out and set as being autofilled.
250 EXPECT_EQ(password
, first_password_element
.value());
251 EXPECT_EQ(password
, second_password_element
.value());
252 EXPECT_TRUE(first_password_element
.isAutofilled());
253 EXPECT_TRUE(second_password_element
.isAutofilled());
255 // Make sure onchange events are called.
256 int first_onchange_called
= -1;
257 int second_onchange_called
= -1;
259 ExecuteJavaScriptAndReturnIntValue(
260 base::ASCIIToUTF16("firstOnChangeCalled ? 1 : 0"),
261 &first_onchange_called
));
262 EXPECT_EQ(1, first_onchange_called
);
264 ExecuteJavaScriptAndReturnIntValue(
265 base::ASCIIToUTF16("secondOnChangeCalled ? 1 : 0"),
266 &second_onchange_called
));
267 EXPECT_EQ(1, second_onchange_called
);
269 // Focus moved to the next input field.
270 // TODO(zysxqn): Change this back to the address element once Bug 90224
271 // https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
272 element
= document
.getElementById(WebString::fromUTF8("first_password"));
273 ASSERT_FALSE(element
.isNull());
274 EXPECT_EQ(element
, document
.focusedElement());
277 TEST_F(PasswordGenerationAgentTest
, EditingTest
) {
278 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
279 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
280 SetAccountCreationFormsDetectedMessage(password_generation_
,
281 GetMainFrame()->document(),
284 WebDocument document
= GetMainFrame()->document();
286 document
.getElementById(WebString::fromUTF8("first_password"));
287 ASSERT_FALSE(element
.isNull());
288 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
289 element
= document
.getElementById(WebString::fromUTF8("second_password"));
290 ASSERT_FALSE(element
.isNull());
291 WebInputElement second_password_element
= element
.to
<WebInputElement
>();
293 base::string16 password
= base::ASCIIToUTF16("random_password");
294 AutofillMsg_GeneratedPasswordAccepted
msg(0, password
);
295 password_generation_
->OnMessageReceived(msg
);
297 // Passwords start out the same.
298 EXPECT_EQ(password
, first_password_element
.value());
299 EXPECT_EQ(password
, second_password_element
.value());
301 // After editing the first field they are still the same.
302 std::string edited_password_ascii
= "edited_password";
303 SimulateUserInputChangeForElement(&first_password_element
,
304 edited_password_ascii
);
305 base::string16 edited_password
= base::ASCIIToUTF16(edited_password_ascii
);
306 EXPECT_EQ(edited_password
, first_password_element
.value());
307 EXPECT_EQ(edited_password
, second_password_element
.value());
309 // Clear any uninteresting sent messages.
310 render_thread_
->sink().ClearMessages();
312 // Verify that password mirroring works correctly even when the password
314 SimulateUserInputChangeForElement(&first_password_element
, std::string());
315 EXPECT_EQ(base::string16(), first_password_element
.value());
316 EXPECT_EQ(base::string16(), second_password_element
.value());
318 // Should have notified the browser that the password is no longer generated
319 // and trigger generation again.
320 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
321 AutofillHostMsg_PasswordNoLongerGenerated::ID
));
322 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
323 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
326 TEST_F(PasswordGenerationAgentTest
, BlacklistedTest
) {
327 // Did not receive not blacklisted message. Don't show password generation
329 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
330 SetAccountCreationFormsDetectedMessage(password_generation_
,
331 GetMainFrame()->document(),
333 ExpectGenerationAvailable("first_password", false);
335 // Receive one not blacklisted message for non account creation form. Don't
336 // show password generation icon.
337 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
338 SetNotBlacklistedMessage(password_generation_
, kSigninFormHTML
);
339 SetAccountCreationFormsDetectedMessage(password_generation_
,
340 GetMainFrame()->document(),
342 ExpectGenerationAvailable("first_password", false);
344 // Receive one not blacklisted message for account creation form. Show
345 // password generation icon.
346 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
347 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
348 SetAccountCreationFormsDetectedMessage(password_generation_
,
349 GetMainFrame()->document(),
351 ExpectGenerationAvailable("first_password", true);
353 // Receive two not blacklisted messages, one is for account creation form and
354 // the other is not. Show password generation icon.
355 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
356 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
357 SetNotBlacklistedMessage(password_generation_
, kSigninFormHTML
);
358 SetAccountCreationFormsDetectedMessage(password_generation_
,
359 GetMainFrame()->document(),
361 ExpectGenerationAvailable("first_password", true);
364 TEST_F(PasswordGenerationAgentTest
, AccountCreationFormsDetectedTest
) {
365 // Did not receive account creation forms detected message. Don't show
366 // password generation icon.
367 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
368 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
369 ExpectGenerationAvailable("first_password", false);
371 // Receive the account creation forms detected message. Show password
373 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
374 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
375 SetAccountCreationFormsDetectedMessage(password_generation_
,
376 GetMainFrame()->document(),
378 ExpectGenerationAvailable("first_password", true);
381 TEST_F(PasswordGenerationAgentTest
, MaximumOfferSize
) {
382 base::HistogramTester histogram_tester
;
384 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
385 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
386 SetAccountCreationFormsDetectedMessage(password_generation_
,
387 GetMainFrame()->document(),
389 ExpectGenerationAvailable("first_password", true);
391 WebDocument document
= GetMainFrame()->document();
393 document
.getElementById(WebString::fromUTF8("first_password"));
394 ASSERT_FALSE(element
.isNull());
395 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
397 // Make a password just under maximum offer size.
398 SimulateUserInputChangeForElement(
399 &first_password_element
,
400 std::string(password_generation_
->kMaximumOfferSize
- 1, 'a'));
401 // There should now be a message to show the UI.
402 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
403 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
404 render_thread_
->sink().ClearMessages();
406 // Simulate a user typing a password just over maximum offer size.
407 SimulateUserTypingASCIICharacter('a', false);
408 SimulateUserTypingASCIICharacter('a', true);
409 // There should now be a message to hide the UI.
410 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
411 AutofillHostMsg_HidePasswordGenerationPopup::ID
));
412 render_thread_
->sink().ClearMessages();
414 // Simulate the user deleting characters. The generation popup should be shown
416 SimulateUserTypingASCIICharacter(ui::VKEY_BACK
, true);
417 // There should now be a message to show the UI.
418 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
419 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
420 render_thread_
->sink().ClearMessages();
422 // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
423 // so no messages are sent.
424 ExecuteJavaScriptForTests("document.getElementById('username').focus();");
425 EXPECT_FALSE(render_thread_
->sink().GetFirstMessageMatching(
426 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
427 render_thread_
->sink().ClearMessages();
429 // Focusing the password field will bring up the generation UI again.
430 ExecuteJavaScriptForTests(
431 "document.getElementById('first_password').focus();");
432 EXPECT_TRUE(render_thread_
->sink().GetFirstMessageMatching(
433 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
434 render_thread_
->sink().ClearMessages();
436 // Loading a different page triggers UMA stat upload. Verify that only one
437 // display event is sent even though
438 LoadHTMLWithUserGesture(kSigninFormHTML
);
440 histogram_tester
.ExpectBucketCount(
441 "PasswordGeneration.Event",
442 autofill::password_generation::GENERATION_POPUP_SHOWN
,
446 TEST_F(PasswordGenerationAgentTest
, DynamicFormTest
) {
447 LoadHTMLWithUserGesture(kSigninFormHTML
);
448 SetNotBlacklistedMessage(password_generation_
, kSigninFormHTML
);
450 ExecuteJavaScriptForTests(
451 "var form = document.createElement('form');"
452 "var username = document.createElement('input');"
453 "username.type = 'text';"
454 "username.id = 'dynamic_username';"
455 "var first_password = document.createElement('input');"
456 "first_password.type = 'password';"
457 "first_password.id = 'first_password';"
458 "first_password.name = 'first_password';"
459 "var second_password = document.createElement('input');"
460 "second_password.type = 'password';"
461 "second_password.id = 'second_password';"
462 "second_password.name = 'second_password';"
463 "form.appendChild(username);"
464 "form.appendChild(first_password);"
465 "form.appendChild(second_password);"
466 "document.body.appendChild(form);");
467 ProcessPendingMessages();
469 // This needs to come after the DOM has been modified.
470 SetAccountCreationFormsDetectedMessage(password_generation_
,
471 GetMainFrame()->document(),
474 // TODO(gcasto): I'm slightly worried about flakes in this test where
475 // didAssociateFormControls() isn't called. If this turns out to be a problem
476 // adding a call to OnDynamicFormsSeen(GetMainFrame()) will fix it, though
477 // it will weaken the test.
478 ExpectGenerationAvailable("first_password", true);
481 TEST_F(PasswordGenerationAgentTest
, MultiplePasswordFormsTest
) {
482 // If two forms on the page looks like possible account creation forms, make
483 // sure to trigger on the one that is specified from Autofill.
484 LoadHTMLWithUserGesture(kMultipleAccountCreationFormHTML
);
485 SetNotBlacklistedMessage(password_generation_
,
486 kMultipleAccountCreationFormHTML
);
488 // Should trigger on the second form.
489 SetAccountCreationFormsDetectedMessage(password_generation_
,
490 GetMainFrame()->document(),
493 ExpectGenerationAvailable("password", false);
494 ExpectGenerationAvailable("first_password", true);
497 TEST_F(PasswordGenerationAgentTest
, MessagesAfterAccountSignupFormFound
) {
498 LoadHTMLWithUserGesture(kAccountCreationFormHTML
);
499 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
500 SetAccountCreationFormsDetectedMessage(password_generation_
,
501 GetMainFrame()->document(),
504 // Generation should be enabled.
505 ExpectGenerationAvailable("first_password", true);
507 // Extra not blacklisted messages can be sent. Make sure that they are handled
508 // correctly (generation should still be available).
509 SetNotBlacklistedMessage(password_generation_
, kAccountCreationFormHTML
);
511 // Need to focus another field first for verification to work.
512 ExpectGenerationAvailable("second_password", false);
513 ExpectGenerationAvailable("first_password", true);
516 // Losing focus should not trigger a password generation popup.
517 TEST_F(PasswordGenerationAgentTest
, BlurTest
) {
518 render_thread_
->sink().ClearMessages();
519 LoadHTMLWithUserGesture(kDisabledElementAccountCreationFormHTML
);
520 SetNotBlacklistedMessage(password_generation_
,
521 kDisabledElementAccountCreationFormHTML
);
522 SetAccountCreationFormsDetectedMessage(password_generation_
,
523 GetMainFrame()->document(),
526 // Focus on the first password field: password generation popup should show
528 ExpectGenerationAvailable("first_password", true);
530 // Remove focus from everywhere by clicking an unfocusable element: password
531 // generation popup should not show up.
532 EXPECT_TRUE(SimulateElementClick("disabled"));
533 EXPECT_FALSE(render_thread_
->sink().GetFirstMessageMatching(
534 AutofillHostMsg_GenerationAvailableForForm::ID
));
535 EXPECT_FALSE(render_thread_
->sink().GetFirstMessageMatching(
536 AutofillHostMsg_ShowPasswordGenerationPopup::ID
));
539 TEST_F(PasswordGenerationAgentTest
, AutocompleteAttributesTest
) {
540 // Verify that autocomplete attributes can override Autofill to enable
542 LoadHTMLWithUserGesture(kBothAutocompleteAttributesFormHTML
);
543 SetNotBlacklistedMessage(password_generation_
,
544 kBothAutocompleteAttributesFormHTML
);
546 ExpectGenerationAvailable("first_password", true);
548 // Only setting one of the two attributes doesn't trigger generation.
549 LoadHTMLWithUserGesture(kUsernameAutocompleteAttributeFormHTML
);
550 SetNotBlacklistedMessage(password_generation_
,
551 kUsernameAutocompleteAttributeFormHTML
);
553 ExpectGenerationAvailable("first_password", false);
555 LoadHTMLWithUserGesture(kNewPasswordAutocompleteAttributeFormHTML
);
556 SetNotBlacklistedMessage(password_generation_
,
557 kNewPasswordAutocompleteAttributeFormHTML
);
559 ExpectGenerationAvailable("first_password", false);
562 } // namespace autofill