Return Windows error code when create-process fails.
[chromium-blink-merge.git] / chrome / renderer / autofill / password_generation_agent_browsertest.cc
blob3fc06a1e87b108a1d2120bc759a471b8e652fc74
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.
5 #include <string.h>
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;
27 using blink::WebNode;
28 using blink::WebString;
30 namespace autofill {
32 class PasswordGenerationAgentTest : public ChromeRenderViewTest {
33 public:
34 PasswordGenerationAgentTest() {}
36 void TearDown() override {
37 LoadHTML("");
38 ChromeRenderViewTest::TearDown();
41 void LoadHTMLWithUserGesture(const char* html) {
42 LoadHTML(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();",
56 element_id).c_str());
59 void ExpectGenerationAvailable(const char* element_id,
60 bool available) {
61 FocusField(element_id);
62 ExpectPasswordGenerationAvailable(password_generation_,
63 available);
66 private:
67 DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest);
70 const char kSigninFormHTML[] =
71 "<FORM name = 'blah' action = 'http://www.random.com/'> "
72 " <INPUT type = 'text' id = 'username'/> "
73 " <INPUT type = 'password' id = 'password'/> "
74 " <INPUT type = 'button' id = 'dummy'/> "
75 " <INPUT type = 'submit' value = 'LOGIN' />"
76 "</FORM>";
78 const char kAccountCreationFormHTML[] =
79 "<FORM name = 'blah' action = 'http://www.random.com/'> "
80 " <INPUT type = 'text' id = 'username'/> "
81 " <INPUT type = 'password' id = 'first_password' "
82 " autocomplete = 'off' size = 5/>"
83 " <INPUT type = 'password' id = 'second_password' size = 5/> "
84 " <INPUT type = 'text' id = 'address'/> "
85 " <INPUT type = 'button' id = 'dummy'/> "
86 " <INPUT type = 'submit' value = 'LOGIN' />"
87 "</FORM>";
89 const char kDisabledElementAccountCreationFormHTML[] =
90 "<FORM name = 'blah' action = 'http://www.random.com/'> "
91 " <INPUT type = 'text' id = 'username'/> "
92 " <INPUT type = 'password' id = 'first_password' "
93 " autocomplete = 'off' size = 5/>"
94 " <INPUT type = 'password' id = 'second_password' size = 5/> "
95 " <INPUT type = 'text' id = 'address'/> "
96 " <INPUT type = 'text' id = 'disabled' disabled/> "
97 " <INPUT type = 'button' id = 'dummy'/> "
98 " <INPUT type = 'submit' value = 'LOGIN' />"
99 "</FORM>";
101 const char kHiddenPasswordAccountCreationFormHTML[] =
102 "<FORM name = 'blah' action = 'http://www.random.com/'> "
103 " <INPUT type = 'text' id = 'username'/> "
104 " <INPUT type = 'password' id = 'first_password'/> "
105 " <INPUT type = 'password' id = 'second_password' style='display:none'/> "
106 " <INPUT type = 'button' id = 'dummy'/> "
107 " <INPUT type = 'submit' value = 'LOGIN' />"
108 "</FORM>";
110 const char kInvalidActionAccountCreationFormHTML[] =
111 "<FORM name = 'blah' action = 'invalid'> "
112 " <INPUT type = 'text' id = 'username'/> "
113 " <INPUT type = 'password' id = 'first_password'/> "
114 " <INPUT type = 'password' id = 'second_password'/> "
115 " <INPUT type = 'button' id = 'dummy'/> "
116 " <INPUT type = 'submit' value = 'LOGIN' />"
117 "</FORM>";
119 const char kMultipleAccountCreationFormHTML[] =
120 "<FORM name = 'login' action = 'http://www.random.com/'> "
121 " <INPUT type = 'text' id = 'random'/> "
122 " <INPUT type = 'text' id = 'username'/> "
123 " <INPUT type = 'password' id = 'password'/> "
124 " <INPUT type = 'button' id = 'dummy'/> "
125 " <INPUT type = 'submit' value = 'LOGIN' />"
126 "</FORM>"
127 "<FORM name = 'signup' action = 'http://www.random.com/signup'> "
128 " <INPUT type = 'text' id = 'username'/> "
129 " <INPUT type = 'password' id = 'first_password' "
130 " autocomplete = 'off' size = 5/>"
131 " <INPUT type = 'password' id = 'second_password' size = 5/> "
132 " <INPUT type = 'text' id = 'address'/> "
133 " <INPUT type = 'submit' value = 'LOGIN' />"
134 "</FORM>";
136 const char kBothAutocompleteAttributesFormHTML[] =
137 "<FORM name = 'blah' action = 'http://www.random.com/'> "
138 " <INPUT type = 'text' autocomplete='username' id = 'username'/> "
139 " <INPUT type = 'password' id = 'first_password' "
140 " autocomplete = 'new-password' size = 5/>"
141 " <INPUT type = 'password' id = 'second_password' size = 5/> "
142 " <INPUT type = 'button' id = 'dummy'/> "
143 " <INPUT type = 'submit' value = 'LOGIN' />"
144 "</FORM>";
146 const char kUsernameAutocompleteAttributeFormHTML[] =
147 "<FORM name = 'blah' action = 'http://www.random.com/'> "
148 " <INPUT type = 'text' autocomplete='username' id = 'username'/> "
149 " <INPUT type = 'password' id = 'first_password' size = 5/>"
150 " <INPUT type = 'password' id = 'second_password' size = 5/> "
151 " <INPUT type = 'button' id = 'dummy'/> "
152 " <INPUT type = 'submit' value = 'LOGIN' />"
153 "</FORM>";
155 const char kNewPasswordAutocompleteAttributeFormHTML[] =
156 "<FORM name = 'blah' action = 'http://www.random.com/'> "
157 " <INPUT type = 'text' id = 'username'/> "
158 " <INPUT type = 'password' id = 'first_password' "
159 " autocomplete='new-password' size = 5/>"
160 " <INPUT type = 'password' id = 'second_password' size = 5/> "
161 " <INPUT type = 'button' id = 'dummy'/> "
162 " <INPUT type = 'submit' value = 'LOGIN' />"
163 "</FORM>";
165 const char ChangeDetectionScript[] =
166 "<script>"
167 " firstOnChangeCalled = false;"
168 " secondOnChangeCalled = false;"
169 " document.getElementById('first_password').onchange = function() {"
170 " firstOnChangeCalled = true;"
171 " };"
172 " document.getElementById('second_password').onchange = function() {"
173 " secondOnChangeCalled = true;"
174 " };"
175 "</script>";
177 TEST_F(PasswordGenerationAgentTest, DetectionTest) {
178 // Don't shown the icon for non account creation forms.
179 LoadHTMLWithUserGesture(kSigninFormHTML);
180 ExpectGenerationAvailable("password", false);
182 // We don't show the decoration yet because the feature isn't enabled.
183 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
184 ExpectGenerationAvailable("first_password", false);
186 // Pretend like We have received message indicating site is not blacklisted,
187 // and we have received message indicating the form is classified as
188 // ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
189 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
190 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
191 SetAccountCreationFormsDetectedMessage(password_generation_,
192 GetMainFrame()->document(),
194 ExpectGenerationAvailable("first_password", true);
196 // Hidden fields are not treated differently.
197 LoadHTMLWithUserGesture(kHiddenPasswordAccountCreationFormHTML);
198 SetNotBlacklistedMessage(password_generation_,
199 kHiddenPasswordAccountCreationFormHTML);
200 SetAccountCreationFormsDetectedMessage(password_generation_,
201 GetMainFrame()->document(),
203 ExpectGenerationAvailable("first_password", true);
205 // This doesn't trigger because the form action is invalid.
206 LoadHTMLWithUserGesture(kInvalidActionAccountCreationFormHTML);
207 SetNotBlacklistedMessage(password_generation_,
208 kInvalidActionAccountCreationFormHTML);
209 SetAccountCreationFormsDetectedMessage(password_generation_,
210 GetMainFrame()->document(),
212 ExpectGenerationAvailable("first_password", false);
215 TEST_F(PasswordGenerationAgentTest, FillTest) {
216 // Make sure that we are enabled before loading HTML.
217 std::string html = std::string(kAccountCreationFormHTML) +
218 ChangeDetectionScript;
219 LoadHTMLWithUserGesture(html.c_str());
220 SetNotBlacklistedMessage(password_generation_, html.c_str());
221 SetAccountCreationFormsDetectedMessage(password_generation_,
222 GetMainFrame()->document(),
225 WebDocument document = GetMainFrame()->document();
226 WebElement element =
227 document.getElementById(WebString::fromUTF8("first_password"));
228 ASSERT_FALSE(element.isNull());
229 WebInputElement first_password_element = element.to<WebInputElement>();
230 element = document.getElementById(WebString::fromUTF8("second_password"));
231 ASSERT_FALSE(element.isNull());
232 WebInputElement second_password_element = element.to<WebInputElement>();
234 // Both password fields should be empty.
235 EXPECT_TRUE(first_password_element.value().isNull());
236 EXPECT_TRUE(second_password_element.value().isNull());
238 base::string16 password = base::ASCIIToUTF16("random_password");
239 AutofillMsg_GeneratedPasswordAccepted msg(0, password);
240 password_generation_->OnMessageReceived(msg);
242 // Password fields are filled out and set as being autofilled.
243 EXPECT_EQ(password, first_password_element.value());
244 EXPECT_EQ(password, second_password_element.value());
245 EXPECT_TRUE(first_password_element.isAutofilled());
246 EXPECT_TRUE(second_password_element.isAutofilled());
248 // Make sure onchange events are called.
249 int first_onchange_called = -1;
250 int second_onchange_called = -1;
251 ASSERT_TRUE(
252 ExecuteJavaScriptAndReturnIntValue(
253 base::ASCIIToUTF16("firstOnChangeCalled ? 1 : 0"),
254 &first_onchange_called));
255 EXPECT_EQ(1, first_onchange_called);
256 ASSERT_TRUE(
257 ExecuteJavaScriptAndReturnIntValue(
258 base::ASCIIToUTF16("secondOnChangeCalled ? 1 : 0"),
259 &second_onchange_called));
260 EXPECT_EQ(1, second_onchange_called);
262 // Focus moved to the next input field.
263 // TODO(zysxqn): Change this back to the address element once Bug 90224
264 // https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
265 element = document.getElementById(WebString::fromUTF8("first_password"));
266 ASSERT_FALSE(element.isNull());
267 EXPECT_EQ(element, document.focusedElement());
270 TEST_F(PasswordGenerationAgentTest, EditingTest) {
271 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
272 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
273 SetAccountCreationFormsDetectedMessage(password_generation_,
274 GetMainFrame()->document(),
277 WebDocument document = GetMainFrame()->document();
278 WebElement element =
279 document.getElementById(WebString::fromUTF8("first_password"));
280 ASSERT_FALSE(element.isNull());
281 WebInputElement first_password_element = element.to<WebInputElement>();
282 element = document.getElementById(WebString::fromUTF8("second_password"));
283 ASSERT_FALSE(element.isNull());
284 WebInputElement second_password_element = element.to<WebInputElement>();
286 base::string16 password = base::ASCIIToUTF16("random_password");
287 AutofillMsg_GeneratedPasswordAccepted msg(0, password);
288 password_generation_->OnMessageReceived(msg);
290 // Passwords start out the same.
291 EXPECT_EQ(password, first_password_element.value());
292 EXPECT_EQ(password, second_password_element.value());
294 // After editing the first field they are still the same.
295 std::string edited_password_ascii = "edited_password";
296 SimulateUserInputChangeForElement(&first_password_element,
297 edited_password_ascii);
298 base::string16 edited_password = base::ASCIIToUTF16(edited_password_ascii);
299 EXPECT_EQ(edited_password, first_password_element.value());
300 EXPECT_EQ(edited_password, second_password_element.value());
302 // Clear any uninteresting sent messages.
303 password_generation_->clear_messages();
305 // Verify that password mirroring works correctly even when the password
306 // is deleted.
307 SimulateUserInputChangeForElement(&first_password_element, std::string());
308 EXPECT_EQ(base::string16(), first_password_element.value());
309 EXPECT_EQ(base::string16(), second_password_element.value());
311 // Should have notified the browser that the password is no longer generated
312 // and trigger generation again.
313 ASSERT_EQ(2u, password_generation_->messages().size());
314 EXPECT_EQ(AutofillHostMsg_PasswordNoLongerGenerated::ID,
315 password_generation_->messages()[0]->type());
316 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
317 password_generation_->messages()[1]->type());
320 TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
321 // Did not receive not blacklisted message. Don't show password generation
322 // icon.
323 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
324 SetAccountCreationFormsDetectedMessage(password_generation_,
325 GetMainFrame()->document(),
327 ExpectGenerationAvailable("first_password", false);
329 // Receive one not blacklisted message for non account creation form. Don't
330 // show password generation icon.
331 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
332 SetNotBlacklistedMessage(password_generation_, kSigninFormHTML);
333 SetAccountCreationFormsDetectedMessage(password_generation_,
334 GetMainFrame()->document(),
336 ExpectGenerationAvailable("first_password", false);
338 // Receive one not blacklisted message for account creation form. Show
339 // password generation icon.
340 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
341 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
342 SetAccountCreationFormsDetectedMessage(password_generation_,
343 GetMainFrame()->document(),
345 ExpectGenerationAvailable("first_password", true);
347 // Receive two not blacklisted messages, one is for account creation form and
348 // the other is not. Show password generation icon.
349 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
350 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
351 SetNotBlacklistedMessage(password_generation_, kSigninFormHTML);
352 SetAccountCreationFormsDetectedMessage(password_generation_,
353 GetMainFrame()->document(),
355 ExpectGenerationAvailable("first_password", true);
358 TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
359 // Did not receive account creation forms detected message. Don't show
360 // password generation icon.
361 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
362 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
363 ExpectGenerationAvailable("first_password", false);
365 // Receive the account creation forms detected message. Show password
366 // generation icon.
367 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
368 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
369 SetAccountCreationFormsDetectedMessage(password_generation_,
370 GetMainFrame()->document(),
372 ExpectGenerationAvailable("first_password", true);
375 TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
376 base::HistogramTester histogram_tester;
378 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
379 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
380 SetAccountCreationFormsDetectedMessage(password_generation_,
381 GetMainFrame()->document(),
383 ExpectGenerationAvailable("first_password", true);
385 WebDocument document = GetMainFrame()->document();
386 WebElement element =
387 document.getElementById(WebString::fromUTF8("first_password"));
388 ASSERT_FALSE(element.isNull());
389 WebInputElement first_password_element = element.to<WebInputElement>();
391 // Make a password just under maximum offer size.
392 SimulateUserInputChangeForElement(
393 &first_password_element,
394 std::string(password_generation_->kMaximumOfferSize - 1, 'a'));
395 // There should now be a message to show the UI.
396 ASSERT_EQ(1u, password_generation_->messages().size());
397 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
398 password_generation_->messages()[0]->type());
399 password_generation_->clear_messages();
401 // Simulate a user typing a password just over maximum offer size.
402 SimulateUserTypingASCIICharacter('a', false);
403 SimulateUserTypingASCIICharacter('a', true);
404 // There should now be a message to hide the UI.
405 ASSERT_EQ(1u, password_generation_->messages().size());
406 EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID,
407 password_generation_->messages()[0]->type());
408 password_generation_->clear_messages();
410 // Simulate the user deleting characters. The generation popup should be shown
411 // again.
412 SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
413 // There should now be a message to show the UI.
414 ASSERT_EQ(1u, password_generation_->messages().size());
415 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
416 password_generation_->messages()[0]->type());
417 password_generation_->clear_messages();
419 // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
420 // so no messages are sent.
421 ExecuteJavaScriptForTests("document.getElementById('username').focus();");
422 EXPECT_EQ(0u, password_generation_->messages().size());
423 password_generation_->clear_messages();
425 // Focusing the password field will bring up the generation UI again.
426 ExecuteJavaScriptForTests(
427 "document.getElementById('first_password').focus();");
428 ASSERT_EQ(1u, password_generation_->messages().size());
429 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
430 password_generation_->messages()[0]->type());
431 password_generation_->clear_messages();
433 // Loading a different page triggers UMA stat upload. Verify that only one
434 // display event is sent even though
435 LoadHTMLWithUserGesture(kSigninFormHTML);
437 histogram_tester.ExpectBucketCount(
438 "PasswordGeneration.Event",
439 autofill::password_generation::GENERATION_POPUP_SHOWN,
443 TEST_F(PasswordGenerationAgentTest, DynamicFormTest) {
444 LoadHTMLWithUserGesture(kSigninFormHTML);
445 SetNotBlacklistedMessage(password_generation_, kSigninFormHTML);
447 ExecuteJavaScriptForTests(
448 "var form = document.createElement('form');"
449 "var username = document.createElement('input');"
450 "username.type = 'text';"
451 "username.id = 'dynamic_username';"
452 "var first_password = document.createElement('input');"
453 "first_password.type = 'password';"
454 "first_password.id = 'first_password';"
455 "first_password.name = 'first_password';"
456 "var second_password = document.createElement('input');"
457 "second_password.type = 'password';"
458 "second_password.id = 'second_password';"
459 "second_password.name = 'second_password';"
460 "form.appendChild(username);"
461 "form.appendChild(first_password);"
462 "form.appendChild(second_password);"
463 "document.body.appendChild(form);");
464 ProcessPendingMessages();
466 // This needs to come after the DOM has been modified.
467 SetAccountCreationFormsDetectedMessage(password_generation_,
468 GetMainFrame()->document(),
471 // TODO(gcasto): I'm slightly worried about flakes in this test where
472 // didAssociateFormControls() isn't called. If this turns out to be a problem
473 // adding a call to OnDynamicFormsSeen(GetMainFrame()) will fix it, though
474 // it will weaken the test.
475 ExpectGenerationAvailable("first_password", true);
478 TEST_F(PasswordGenerationAgentTest, MultiplePasswordFormsTest) {
479 // If two forms on the page looks like possible account creation forms, make
480 // sure to trigger on the one that is specified from Autofill.
481 LoadHTMLWithUserGesture(kMultipleAccountCreationFormHTML);
482 SetNotBlacklistedMessage(password_generation_,
483 kMultipleAccountCreationFormHTML);
485 // Should trigger on the second form.
486 SetAccountCreationFormsDetectedMessage(password_generation_,
487 GetMainFrame()->document(),
490 ExpectGenerationAvailable("password", false);
491 ExpectGenerationAvailable("first_password", true);
494 TEST_F(PasswordGenerationAgentTest, MessagesAfterAccountSignupFormFound) {
495 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
496 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
497 SetAccountCreationFormsDetectedMessage(password_generation_,
498 GetMainFrame()->document(),
501 // Generation should be enabled.
502 ExpectGenerationAvailable("first_password", true);
504 // Extra not blacklisted messages can be sent. Make sure that they are handled
505 // correctly (generation should still be available).
506 SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
508 // Need to focus another field first for verification to work.
509 ExpectGenerationAvailable("second_password", false);
510 ExpectGenerationAvailable("first_password", true);
513 // Losing focus should not trigger a password generation popup.
514 TEST_F(PasswordGenerationAgentTest, BlurTest) {
515 LoadHTMLWithUserGesture(kDisabledElementAccountCreationFormHTML);
516 SetNotBlacklistedMessage(password_generation_,
517 kDisabledElementAccountCreationFormHTML);
518 SetAccountCreationFormsDetectedMessage(password_generation_,
519 GetMainFrame()->document(),
522 // Focus on the first password field: password generation popup should show
523 // up.
524 ExpectGenerationAvailable("first_password", true);
526 // Remove focus from everywhere by clicking an unfocusable element: password
527 // generation popup should not show up.
528 EXPECT_TRUE(SimulateElementClick("disabled"));
529 EXPECT_EQ(0u, password_generation_->messages().size());
532 TEST_F(PasswordGenerationAgentTest, AutocompleteAttributesTest) {
533 // Verify that autocomplete attributes can override Autofill to enable
534 // generation
535 LoadHTMLWithUserGesture(kBothAutocompleteAttributesFormHTML);
536 SetNotBlacklistedMessage(password_generation_,
537 kBothAutocompleteAttributesFormHTML);
539 ExpectGenerationAvailable("first_password", true);
541 // Only setting one of the two attributes doesn't trigger generation.
542 LoadHTMLWithUserGesture(kUsernameAutocompleteAttributeFormHTML);
543 SetNotBlacklistedMessage(password_generation_,
544 kUsernameAutocompleteAttributeFormHTML);
546 ExpectGenerationAvailable("first_password", false);
548 LoadHTMLWithUserGesture(kNewPasswordAutocompleteAttributeFormHTML);
549 SetNotBlacklistedMessage(password_generation_,
550 kNewPasswordAutocompleteAttributeFormHTML);
552 ExpectGenerationAvailable("first_password", false);
555 } // namespace autofill