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 "chrome/test/base/chrome_render_view_test.h"
10 #include "components/autofill/content/common/autofill_messages.h"
11 #include "components/autofill/content/renderer/autofill_agent.h"
12 #include "components/autofill/content/renderer/test_password_generation_agent.h"
13 #include "components/autofill/core/common/form_data.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/WebKit/public/platform/WebString.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
18 #include "third_party/WebKit/public/web/WebWidget.h"
20 using blink::WebDocument
;
21 using blink::WebElement
;
22 using blink::WebInputElement
;
24 using blink::WebString
;
28 class PasswordGenerationAgentTest
: public ChromeRenderViewTest
{
30 PasswordGenerationAgentTest() {}
32 virtual void TearDown() {
34 ChromeRenderViewTest::TearDown();
37 void SetNotBlacklistedMessage(const char* form_str
) {
38 autofill::PasswordForm form
;
40 GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str
));
41 AutofillMsg_FormNotBlacklisted
msg(0, form
);
42 password_generation_
->OnMessageReceived(msg
);
45 void SetAccountCreationFormsDetectedMessage(const char* form_str
) {
46 autofill::FormData form
;
48 GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str
));
49 std::vector
<autofill::FormData
> forms
;
50 forms
.push_back(form
);
51 AutofillMsg_AccountCreationFormsDetected
msg(0, forms
);
52 password_generation_
->OnMessageReceived(msg
);
55 void ExpectPasswordGenerationAvailable(const char* element_id
,
57 WebDocument document
= GetMainFrame()->document();
59 document
.getElementById(WebString::fromUTF8(element_id
));
60 ASSERT_FALSE(element
.isNull());
62 base::StringPrintf("document.getElementById('%s').focus();",
65 ASSERT_EQ(1u, password_generation_
->messages().size());
66 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID
,
67 password_generation_
->messages()[0]->type());
69 EXPECT_EQ(0u, password_generation_
->messages().size());
71 password_generation_
->clear_messages();
75 DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest
);
78 const char kSigninFormHTML
[] =
79 "<FORM name = 'blah' action = 'http://www.random.com/'> "
80 " <INPUT type = 'text' id = 'username'/> "
81 " <INPUT type = 'password' id = 'password'/> "
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 = 'submit' value = 'LOGIN' />"
95 const char kHiddenPasswordAccountCreationFormHTML
[] =
96 "<FORM name = 'blah' action = 'http://www.random.com/'> "
97 " <INPUT type = 'text' id = 'username'/> "
98 " <INPUT type = 'password' id = 'first_password'/> "
99 " <INPUT type = 'password' id = 'second_password' style='display:none'/> "
100 " <INPUT type = 'submit' value = 'LOGIN' />"
103 const char kInvalidActionAccountCreationFormHTML
[] =
104 "<FORM name = 'blah' action = 'invalid'> "
105 " <INPUT type = 'text' id = 'username'/> "
106 " <INPUT type = 'password' id = 'first_password'/> "
107 " <INPUT type = 'password' id = 'second_password'/> "
108 " <INPUT type = 'submit' value = 'LOGIN' />"
111 TEST_F(PasswordGenerationAgentTest
, DetectionTest
) {
112 // Don't shown the icon for non account creation forms.
113 LoadHTML(kSigninFormHTML
);
114 ExpectPasswordGenerationAvailable("password", false);
116 // We don't show the decoration yet because the feature isn't enabled.
117 LoadHTML(kAccountCreationFormHTML
);
118 ExpectPasswordGenerationAvailable("first_password", false);
120 // Pretend like We have received message indicating site is not blacklisted,
121 // and we have received message indicating the form is classified as
122 // ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
123 LoadHTML(kAccountCreationFormHTML
);
124 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
125 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
126 ExpectPasswordGenerationAvailable("first_password", true);
128 // This doesn't trigger because hidden password fields are ignored.
129 LoadHTML(kHiddenPasswordAccountCreationFormHTML
);
130 SetNotBlacklistedMessage(kHiddenPasswordAccountCreationFormHTML
);
131 SetAccountCreationFormsDetectedMessage(
132 kHiddenPasswordAccountCreationFormHTML
);
133 ExpectPasswordGenerationAvailable("first_password", false);
135 // This doesn't trigger because the form action is invalid.
136 LoadHTML(kInvalidActionAccountCreationFormHTML
);
137 SetNotBlacklistedMessage(kInvalidActionAccountCreationFormHTML
);
138 SetAccountCreationFormsDetectedMessage(kInvalidActionAccountCreationFormHTML
);
139 ExpectPasswordGenerationAvailable("first_password", false);
142 TEST_F(PasswordGenerationAgentTest
, FillTest
) {
143 // Make sure that we are enabled before loading HTML.
144 LoadHTML(kAccountCreationFormHTML
);
146 WebDocument document
= GetMainFrame()->document();
148 document
.getElementById(WebString::fromUTF8("first_password"));
149 ASSERT_FALSE(element
.isNull());
150 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
151 element
= document
.getElementById(WebString::fromUTF8("second_password"));
152 ASSERT_FALSE(element
.isNull());
153 WebInputElement second_password_element
= element
.to
<WebInputElement
>();
155 // Both password fields should be empty.
156 EXPECT_TRUE(first_password_element
.value().isNull());
157 EXPECT_TRUE(second_password_element
.value().isNull());
159 base::string16 password
= base::ASCIIToUTF16("random_password");
160 AutofillMsg_GeneratedPasswordAccepted
msg(0, password
);
161 password_generation_
->OnMessageReceived(msg
);
163 // Password fields are filled out and set as being autofilled.
164 EXPECT_EQ(password
, first_password_element
.value());
165 EXPECT_EQ(password
, second_password_element
.value());
166 EXPECT_TRUE(first_password_element
.isAutofilled());
167 EXPECT_TRUE(second_password_element
.isAutofilled());
169 // Focus moved to the next input field.
170 // TODO(zysxqn): Change this back to the address element once Bug 90224
171 // https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
172 element
= document
.getElementById(WebString::fromUTF8("first_password"));
173 ASSERT_FALSE(element
.isNull());
174 EXPECT_EQ(element
, document
.focusedElement());
177 TEST_F(PasswordGenerationAgentTest
, EditingTest
) {
178 LoadHTML(kAccountCreationFormHTML
);
179 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
180 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
182 WebDocument document
= GetMainFrame()->document();
184 document
.getElementById(WebString::fromUTF8("first_password"));
185 ASSERT_FALSE(element
.isNull());
186 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
187 element
= document
.getElementById(WebString::fromUTF8("second_password"));
188 ASSERT_FALSE(element
.isNull());
189 WebInputElement second_password_element
= element
.to
<WebInputElement
>();
191 base::string16 password
= base::ASCIIToUTF16("random_password");
192 AutofillMsg_GeneratedPasswordAccepted
msg(0, password
);
193 password_generation_
->OnMessageReceived(msg
);
195 // Passwords start out the same.
196 EXPECT_EQ(password
, first_password_element
.value());
197 EXPECT_EQ(password
, second_password_element
.value());
199 // After editing the first field they are still the same.
200 base::string16 edited_password
= base::ASCIIToUTF16("edited_password");
201 first_password_element
.setValue(edited_password
);
202 // Cast to WebAutofillClient where textFieldDidChange() is public.
203 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->textFieldDidChange(
204 first_password_element
);
205 // textFieldDidChange posts a task, so we need to wait until it's been
207 base::MessageLoop::current()->RunUntilIdle();
209 EXPECT_EQ(edited_password
, first_password_element
.value());
210 EXPECT_EQ(edited_password
, second_password_element
.value());
213 TEST_F(PasswordGenerationAgentTest
, BlacklistedTest
) {
214 // Did not receive not blacklisted message. Don't show password generation
216 LoadHTML(kAccountCreationFormHTML
);
217 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
218 ExpectPasswordGenerationAvailable("first_password", false);
220 // Receive one not blacklisted message for non account creation form. Don't
221 // show password generation icon.
222 LoadHTML(kAccountCreationFormHTML
);
223 SetNotBlacklistedMessage(kSigninFormHTML
);
224 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
225 ExpectPasswordGenerationAvailable("first_password", false);
227 // Receive one not blackliste message for account creation form. Show password
229 LoadHTML(kAccountCreationFormHTML
);
230 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
231 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
232 ExpectPasswordGenerationAvailable("first_password", true);
234 // Receive two not blacklisted messages, one is for account creation form and
235 // the other is not. Show password generation icon.
236 LoadHTML(kAccountCreationFormHTML
);
237 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
238 SetNotBlacklistedMessage(kSigninFormHTML
);
239 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
240 ExpectPasswordGenerationAvailable("first_password", true);
243 TEST_F(PasswordGenerationAgentTest
, AccountCreationFormsDetectedTest
) {
244 // Did not receive account creation forms detected messege. Don't show
245 // password generation icon.
246 LoadHTML(kAccountCreationFormHTML
);
247 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
248 ExpectPasswordGenerationAvailable("first_password", false);
250 // Receive the account creation forms detected message. Show password
252 LoadHTML(kAccountCreationFormHTML
);
253 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
254 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
255 ExpectPasswordGenerationAvailable("first_password", true);
258 TEST_F(PasswordGenerationAgentTest
, MaximumOfferSize
) {
259 LoadHTML(kAccountCreationFormHTML
);
260 SetNotBlacklistedMessage(kAccountCreationFormHTML
);
261 SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML
);
262 ExpectPasswordGenerationAvailable("first_password", true);
264 WebDocument document
= GetMainFrame()->document();
266 document
.getElementById(WebString::fromUTF8("first_password"));
267 ASSERT_FALSE(element
.isNull());
268 WebInputElement first_password_element
= element
.to
<WebInputElement
>();
270 // Make a password just under maximum offer size.
271 first_password_element
.setValue(
273 std::string(password_generation_
->kMaximumOfferSize
- 1, 'a')));
274 // Cast to WebAutofillClient where textFieldDidChange() is public.
275 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->textFieldDidChange(
276 first_password_element
);
277 // textFieldDidChange posts a task, so we need to wait until it's been
279 base::MessageLoop::current()->RunUntilIdle();
280 // There should now be a message to show the UI.
281 ASSERT_EQ(1u, password_generation_
->messages().size());
282 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID
,
283 password_generation_
->messages()[0]->type());
284 password_generation_
->clear_messages();
286 // Simulate a user typing a password just over maximum offer size.
287 first_password_element
.setValue(
289 std::string(password_generation_
->kMaximumOfferSize
+ 1, 'a')));
290 // Cast to WebAutofillClient where textFieldDidChange() is public.
291 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->textFieldDidChange(
292 first_password_element
);
293 // textFieldDidChange posts a task, so we need to wait until it's been
295 base::MessageLoop::current()->RunUntilIdle();
296 // There should now be a message to hide the UI.
297 ASSERT_EQ(1u, password_generation_
->messages().size());
298 EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID
,
299 password_generation_
->messages()[0]->type());
300 password_generation_
->clear_messages();
302 // Simulate the user deleting characters. The generation popup should be shown
304 first_password_element
.setValue(
306 std::string(password_generation_
->kMaximumOfferSize
, 'a')));
307 // Cast to WebAutofillClient where textFieldDidChange() is public.
308 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->textFieldDidChange(
309 first_password_element
);
310 // textFieldDidChange posts a task, so we need to wait until it's been
312 base::MessageLoop::current()->RunUntilIdle();
313 // There should now be a message to show the UI.
314 ASSERT_EQ(1u, password_generation_
->messages().size());
315 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID
,
316 password_generation_
->messages()[0]->type());
317 password_generation_
->clear_messages();
319 // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
320 // so no messages are sent.
321 ExecuteJavaScript("document.getElementById('username').focus();");
322 EXPECT_EQ(0u, password_generation_
->messages().size());
323 password_generation_
->clear_messages();
325 // Focusing the password field will bring up the generation UI again.
326 ExecuteJavaScript("document.getElementById('first_password').focus();");
327 EXPECT_EQ(1u, password_generation_
->messages().size());
328 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID
,
329 password_generation_
->messages()[0]->type());
330 password_generation_
->clear_messages();
333 } // namespace autofill