Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / chrome / renderer / autofill / password_generation_agent_browsertest.cc
bloba093f32e9e8463c9772840af76ef9971f88b87ad
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/test/base/chrome_render_view_test.h"
11 #include "components/autofill/content/common/autofill_messages.h"
12 #include "components/autofill/content/renderer/autofill_agent.h"
13 #include "components/autofill/content/renderer/form_autofill_util.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"
23 using blink::WebDocument;
24 using blink::WebElement;
25 using blink::WebInputElement;
26 using blink::WebNode;
27 using blink::WebString;
29 namespace autofill {
31 class PasswordGenerationAgentTest : public ChromeRenderViewTest {
32 public:
33 PasswordGenerationAgentTest() {}
35 void TearDown() override {
36 LoadHTML("");
37 ChromeRenderViewTest::TearDown();
40 void SetNotBlacklistedMessage(const char* form_str) {
41 autofill::PasswordForm form;
42 form.origin =
43 GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
44 AutofillMsg_FormNotBlacklisted msg(0, form);
45 password_generation_->OnMessageReceived(msg);
48 // Sends a message that the |form_index| form on the page is valid for
49 // account creation.
50 void SetAccountCreationFormsDetectedMessage(int form_index) {
51 WebDocument document = GetMainFrame()->document();
52 blink::WebVector<blink::WebFormElement> web_forms;
53 document.forms(web_forms);
55 autofill::FormData form_data;
56 WebFormElementToFormData(web_forms[form_index],
57 blink::WebFormControlElement(),
58 REQUIRE_NONE,
59 EXTRACT_NONE,
60 &form_data,
61 NULL /* FormFieldData */);
63 std::vector<autofill::FormData> forms;
64 forms.push_back(form_data);
65 AutofillMsg_AccountCreationFormsDetected msg(0, forms);
66 password_generation_->OnMessageReceived(msg);
69 void ExpectPasswordGenerationAvailable(const char* element_id,
70 bool available) {
71 WebDocument document = GetMainFrame()->document();
72 WebElement element =
73 document.getElementById(WebString::fromUTF8(element_id));
74 ASSERT_FALSE(element.isNull());
75 ExecuteJavaScript(
76 base::StringPrintf("document.getElementById('%s').focus();",
77 element_id).c_str());
78 if (available) {
79 ASSERT_EQ(1u, password_generation_->messages().size());
80 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
81 password_generation_->messages()[0]->type());
82 } else {
83 EXPECT_EQ(0u, password_generation_->messages().size());
85 password_generation_->clear_messages();
88 void LoadHTMLWithUserGesture(const char* html) {
89 LoadHTML(html);
91 // Enable show-ime event when element is focused by indicating that a user
92 // gesture has been processed since load.
93 EXPECT_TRUE(SimulateElementClick("dummy"));
96 private:
97 DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest);
100 const char kSigninFormHTML[] =
101 "<FORM name = 'blah' action = 'http://www.random.com/'> "
102 " <INPUT type = 'text' id = 'username'/> "
103 " <INPUT type = 'password' id = 'password'/> "
104 " <INPUT type = 'button' id = 'dummy'/> "
105 " <INPUT type = 'submit' value = 'LOGIN' />"
106 "</FORM>";
108 const char kAccountCreationFormHTML[] =
109 "<FORM name = 'blah' action = 'http://www.random.com/'> "
110 " <INPUT type = 'text' id = 'username'/> "
111 " <INPUT type = 'password' id = 'first_password' "
112 " autocomplete = 'off' size = 5/>"
113 " <INPUT type = 'password' id = 'second_password' size = 5/> "
114 " <INPUT type = 'text' id = 'address'/> "
115 " <INPUT type = 'button' id = 'dummy'/> "
116 " <INPUT type = 'submit' value = 'LOGIN' />"
117 "</FORM>";
119 const char kDisabledElementAccountCreationFormHTML[] =
120 "<FORM name = 'blah' action = 'http://www.random.com/'> "
121 " <INPUT type = 'text' id = 'username'/> "
122 " <INPUT type = 'password' id = 'first_password' "
123 " autocomplete = 'off' size = 5/>"
124 " <INPUT type = 'password' id = 'second_password' size = 5/> "
125 " <INPUT type = 'text' id = 'address'/> "
126 " <INPUT type = 'text' id = 'disabled' disabled/> "
127 " <INPUT type = 'button' id = 'dummy'/> "
128 " <INPUT type = 'submit' value = 'LOGIN' />"
129 "</FORM>";
131 const char kHiddenPasswordAccountCreationFormHTML[] =
132 "<FORM name = 'blah' action = 'http://www.random.com/'> "
133 " <INPUT type = 'text' id = 'username'/> "
134 " <INPUT type = 'password' id = 'first_password'/> "
135 " <INPUT type = 'password' id = 'second_password' style='display:none'/> "
136 " <INPUT type = 'button' id = 'dummy'/> "
137 " <INPUT type = 'submit' value = 'LOGIN' />"
138 "</FORM>";
140 const char kInvalidActionAccountCreationFormHTML[] =
141 "<FORM name = 'blah' action = 'invalid'> "
142 " <INPUT type = 'text' id = 'username'/> "
143 " <INPUT type = 'password' id = 'first_password'/> "
144 " <INPUT type = 'password' id = 'second_password'/> "
145 " <INPUT type = 'button' id = 'dummy'/> "
146 " <INPUT type = 'submit' value = 'LOGIN' />"
147 "</FORM>";
149 const char kMultipleAccountCreationFormHTML[] =
150 "<FORM name = 'login' action = 'http://www.random.com/'> "
151 " <INPUT type = 'text' id = 'random'/> "
152 " <INPUT type = 'text' id = 'username'/> "
153 " <INPUT type = 'password' id = 'password'/> "
154 " <INPUT type = 'button' id = 'dummy'/> "
155 " <INPUT type = 'submit' value = 'LOGIN' />"
156 "</FORM>"
157 "<FORM name = 'signup' action = 'http://www.random.com/signup'> "
158 " <INPUT type = 'text' id = 'username'/> "
159 " <INPUT type = 'password' id = 'first_password' "
160 " autocomplete = 'off' size = 5/>"
161 " <INPUT type = 'password' id = 'second_password' size = 5/> "
162 " <INPUT type = 'text' id = 'address'/> "
163 " <INPUT type = 'submit' value = 'LOGIN' />"
164 "</FORM>";
166 const char ChangeDetectionScript[] =
167 "<script>"
168 " firstOnChangeCalled = false;"
169 " secondOnChangeCalled = false;"
170 " document.getElementById('first_password').onchange = function() {"
171 " firstOnChangeCalled = true;"
172 " };"
173 " document.getElementById('second_password').onchange = function() {"
174 " secondOnChangeCalled = true;"
175 " };"
176 "</script>";
178 TEST_F(PasswordGenerationAgentTest, DetectionTest) {
179 // Don't shown the icon for non account creation forms.
180 LoadHTMLWithUserGesture(kSigninFormHTML);
181 ExpectPasswordGenerationAvailable("password", false);
183 // We don't show the decoration yet because the feature isn't enabled.
184 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
185 ExpectPasswordGenerationAvailable("first_password", false);
187 // Pretend like We have received message indicating site is not blacklisted,
188 // and we have received message indicating the form is classified as
189 // ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
190 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
191 SetNotBlacklistedMessage(kAccountCreationFormHTML);
192 SetAccountCreationFormsDetectedMessage(0);
193 ExpectPasswordGenerationAvailable("first_password", true);
195 // Hidden fields are not treated differently.
196 LoadHTMLWithUserGesture(kHiddenPasswordAccountCreationFormHTML);
197 SetNotBlacklistedMessage(kHiddenPasswordAccountCreationFormHTML);
198 SetAccountCreationFormsDetectedMessage(0);
199 ExpectPasswordGenerationAvailable("first_password", true);
201 // This doesn't trigger because the form action is invalid.
202 LoadHTMLWithUserGesture(kInvalidActionAccountCreationFormHTML);
203 SetNotBlacklistedMessage(kInvalidActionAccountCreationFormHTML);
204 SetAccountCreationFormsDetectedMessage(0);
205 ExpectPasswordGenerationAvailable("first_password", false);
208 TEST_F(PasswordGenerationAgentTest, FillTest) {
209 // Make sure that we are enabled before loading HTML.
210 std::string html = std::string(kAccountCreationFormHTML) +
211 ChangeDetectionScript;
212 LoadHTMLWithUserGesture(html.c_str());
213 SetNotBlacklistedMessage(html.c_str());
214 SetAccountCreationFormsDetectedMessage(0);
216 WebDocument document = GetMainFrame()->document();
217 WebElement element =
218 document.getElementById(WebString::fromUTF8("first_password"));
219 ASSERT_FALSE(element.isNull());
220 WebInputElement first_password_element = element.to<WebInputElement>();
221 element = document.getElementById(WebString::fromUTF8("second_password"));
222 ASSERT_FALSE(element.isNull());
223 WebInputElement second_password_element = element.to<WebInputElement>();
225 // Both password fields should be empty.
226 EXPECT_TRUE(first_password_element.value().isNull());
227 EXPECT_TRUE(second_password_element.value().isNull());
229 base::string16 password = base::ASCIIToUTF16("random_password");
230 AutofillMsg_GeneratedPasswordAccepted msg(0, password);
231 password_generation_->OnMessageReceived(msg);
233 // Password fields are filled out and set as being autofilled.
234 EXPECT_EQ(password, first_password_element.value());
235 EXPECT_EQ(password, second_password_element.value());
236 EXPECT_TRUE(first_password_element.isAutofilled());
237 EXPECT_TRUE(second_password_element.isAutofilled());
239 // Make sure onchange events are called.
240 int first_onchange_called = -1;
241 int second_onchange_called = -1;
242 ASSERT_TRUE(
243 ExecuteJavaScriptAndReturnIntValue(
244 base::ASCIIToUTF16("firstOnChangeCalled ? 1 : 0"),
245 &first_onchange_called));
246 EXPECT_EQ(1, first_onchange_called);
247 ASSERT_TRUE(
248 ExecuteJavaScriptAndReturnIntValue(
249 base::ASCIIToUTF16("secondOnChangeCalled ? 1 : 0"),
250 &second_onchange_called));
251 EXPECT_EQ(1, second_onchange_called);
253 // Focus moved to the next input field.
254 // TODO(zysxqn): Change this back to the address element once Bug 90224
255 // https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
256 element = document.getElementById(WebString::fromUTF8("first_password"));
257 ASSERT_FALSE(element.isNull());
258 EXPECT_EQ(element, document.focusedElement());
261 TEST_F(PasswordGenerationAgentTest, EditingTest) {
262 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
263 SetNotBlacklistedMessage(kAccountCreationFormHTML);
264 SetAccountCreationFormsDetectedMessage(0);
266 WebDocument document = GetMainFrame()->document();
267 WebElement element =
268 document.getElementById(WebString::fromUTF8("first_password"));
269 ASSERT_FALSE(element.isNull());
270 WebInputElement first_password_element = element.to<WebInputElement>();
271 element = document.getElementById(WebString::fromUTF8("second_password"));
272 ASSERT_FALSE(element.isNull());
273 WebInputElement second_password_element = element.to<WebInputElement>();
275 base::string16 password = base::ASCIIToUTF16("random_password");
276 AutofillMsg_GeneratedPasswordAccepted msg(0, password);
277 password_generation_->OnMessageReceived(msg);
279 // Passwords start out the same.
280 EXPECT_EQ(password, first_password_element.value());
281 EXPECT_EQ(password, second_password_element.value());
283 // After editing the first field they are still the same.
284 base::string16 edited_password = base::ASCIIToUTF16("edited_password");
285 first_password_element.setValue(edited_password);
286 // Cast to WebAutofillClient where textFieldDidChange() is public.
287 static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
288 first_password_element);
289 // textFieldDidChange posts a task, so we need to wait until it's been
290 // processed.
291 base::MessageLoop::current()->RunUntilIdle();
292 EXPECT_EQ(edited_password, first_password_element.value());
293 EXPECT_EQ(edited_password, second_password_element.value());
295 // Verify that password mirroring works correctly even when the password
296 // is deleted.
297 base::string16 empty_password;
298 first_password_element.setValue(empty_password);
299 // Cast to WebAutofillClient where textFieldDidChange() is public.
300 static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
301 first_password_element);
302 // textFieldDidChange posts a task, so we need to wait until it's been
303 // processed.
304 base::MessageLoop::current()->RunUntilIdle();
305 EXPECT_EQ(empty_password, first_password_element.value());
306 EXPECT_EQ(empty_password, second_password_element.value());
309 TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
310 // Did not receive not blacklisted message. Don't show password generation
311 // icon.
312 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
313 SetAccountCreationFormsDetectedMessage(0);
314 ExpectPasswordGenerationAvailable("first_password", false);
316 // Receive one not blacklisted message for non account creation form. Don't
317 // show password generation icon.
318 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
319 SetNotBlacklistedMessage(kSigninFormHTML);
320 SetAccountCreationFormsDetectedMessage(0);
321 ExpectPasswordGenerationAvailable("first_password", false);
323 // Receive one not blacklisted message for account creation form. Show
324 // password generation icon.
325 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
326 SetNotBlacklistedMessage(kAccountCreationFormHTML);
327 SetAccountCreationFormsDetectedMessage(0);
328 ExpectPasswordGenerationAvailable("first_password", true);
330 // Receive two not blacklisted messages, one is for account creation form and
331 // the other is not. Show password generation icon.
332 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
333 SetNotBlacklistedMessage(kAccountCreationFormHTML);
334 SetNotBlacklistedMessage(kSigninFormHTML);
335 SetAccountCreationFormsDetectedMessage(0);
336 ExpectPasswordGenerationAvailable("first_password", true);
339 TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
340 // Did not receive account creation forms detected message. Don't show
341 // password generation icon.
342 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
343 SetNotBlacklistedMessage(kAccountCreationFormHTML);
344 ExpectPasswordGenerationAvailable("first_password", false);
346 // Receive the account creation forms detected message. Show password
347 // generation icon.
348 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
349 SetNotBlacklistedMessage(kAccountCreationFormHTML);
350 SetAccountCreationFormsDetectedMessage(0);
351 ExpectPasswordGenerationAvailable("first_password", true);
354 TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
355 base::HistogramTester histogram_tester;
357 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
358 SetNotBlacklistedMessage(kAccountCreationFormHTML);
359 SetAccountCreationFormsDetectedMessage(0);
360 ExpectPasswordGenerationAvailable("first_password", true);
362 WebDocument document = GetMainFrame()->document();
363 WebElement element =
364 document.getElementById(WebString::fromUTF8("first_password"));
365 ASSERT_FALSE(element.isNull());
366 WebInputElement first_password_element = element.to<WebInputElement>();
368 // Make a password just under maximum offer size.
369 first_password_element.setValue(
370 base::ASCIIToUTF16(
371 std::string(password_generation_->kMaximumOfferSize - 1, 'a')));
372 // Cast to WebAutofillClient where textFieldDidChange() is public.
373 static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
374 first_password_element);
375 // textFieldDidChange posts a task, so we need to wait until it's been
376 // processed.
377 base::MessageLoop::current()->RunUntilIdle();
378 // There should now be a message to show the UI.
379 ASSERT_EQ(1u, password_generation_->messages().size());
380 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
381 password_generation_->messages()[0]->type());
382 password_generation_->clear_messages();
384 // Simulate a user typing a password just over maximum offer size.
385 first_password_element.setValue(
386 base::ASCIIToUTF16(
387 std::string(password_generation_->kMaximumOfferSize + 1, 'a')));
388 // Cast to WebAutofillClient where textFieldDidChange() is public.
389 static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
390 first_password_element);
391 // textFieldDidChange posts a task, so we need to wait until it's been
392 // processed.
393 base::MessageLoop::current()->RunUntilIdle();
394 // There should now be a message to hide the UI.
395 ASSERT_EQ(1u, password_generation_->messages().size());
396 EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID,
397 password_generation_->messages()[0]->type());
398 password_generation_->clear_messages();
400 // Simulate the user deleting characters. The generation popup should be shown
401 // again.
402 first_password_element.setValue(
403 base::ASCIIToUTF16(
404 std::string(password_generation_->kMaximumOfferSize, 'a')));
405 // Cast to WebAutofillClient where textFieldDidChange() is public.
406 static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
407 first_password_element);
408 // textFieldDidChange posts a task, so we need to wait until it's been
409 // processed.
410 base::MessageLoop::current()->RunUntilIdle();
411 // There should now be a message to show the UI.
412 ASSERT_EQ(1u, password_generation_->messages().size());
413 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
414 password_generation_->messages()[0]->type());
415 password_generation_->clear_messages();
417 // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
418 // so no messages are sent.
419 ExecuteJavaScript("document.getElementById('username').focus();");
420 EXPECT_EQ(0u, password_generation_->messages().size());
421 password_generation_->clear_messages();
423 // Focusing the password field will bring up the generation UI again.
424 ExecuteJavaScript("document.getElementById('first_password').focus();");
425 ASSERT_EQ(1u, password_generation_->messages().size());
426 EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
427 password_generation_->messages()[0]->type());
428 password_generation_->clear_messages();
430 // Loading a different page triggers UMA stat upload. Verify that only one
431 // display event is sent even though
432 LoadHTMLWithUserGesture(kSigninFormHTML);
434 histogram_tester.ExpectBucketCount(
435 "PasswordGeneration.Event",
436 autofill::password_generation::GENERATION_POPUP_SHOWN,
440 TEST_F(PasswordGenerationAgentTest, DynamicFormTest) {
441 LoadHTMLWithUserGesture(kSigninFormHTML);
442 SetNotBlacklistedMessage(kSigninFormHTML);
444 ExecuteJavaScript(
445 "var form = document.createElement('form');"
446 "var username = document.createElement('input');"
447 "username.type = 'text';"
448 "username.id = 'dynamic_username';"
449 "var first_password = document.createElement('input');"
450 "first_password.type = 'password';"
451 "first_password.id = 'first_password';"
452 "first_password.name = 'first_password';"
453 "var second_password = document.createElement('input');"
454 "second_password.type = 'password';"
455 "second_password.id = 'second_password';"
456 "second_password.name = 'second_password';"
457 "form.appendChild(username);"
458 "form.appendChild(first_password);"
459 "form.appendChild(second_password);"
460 "document.body.appendChild(form);");
461 ProcessPendingMessages();
463 // This needs to come after the DOM has been modified.
464 SetAccountCreationFormsDetectedMessage(1);
466 // TODO(gcasto): I'm slightly worried about flakes in this test where
467 // didAssociateFormControls() isn't called. If this turns out to be a problem
468 // adding a call to OnDynamicFormsSeen(GetMainFrame()) will fix it, though
469 // it will weaken the test.
470 ExpectPasswordGenerationAvailable("first_password", true);
473 TEST_F(PasswordGenerationAgentTest, MultiplePasswordFormsTest) {
474 // If two forms on the page looks like possible account creation forms, make
475 // sure to trigger on the one that is specified from Autofill.
476 LoadHTMLWithUserGesture(kMultipleAccountCreationFormHTML);
477 SetNotBlacklistedMessage(kMultipleAccountCreationFormHTML);
479 // Should trigger on the second form.
480 SetAccountCreationFormsDetectedMessage(1);
482 ExpectPasswordGenerationAvailable("password", false);
483 ExpectPasswordGenerationAvailable("first_password", true);
486 TEST_F(PasswordGenerationAgentTest, MessagesAfterAccountSignupFormFound) {
487 LoadHTMLWithUserGesture(kAccountCreationFormHTML);
488 SetNotBlacklistedMessage(kAccountCreationFormHTML);
489 SetAccountCreationFormsDetectedMessage(0);
491 // Generation should be enabled.
492 ExpectPasswordGenerationAvailable("first_password", true);
494 // Extra not blacklisted messages can be sent. Make sure that they are handled
495 // correctly (generation should still be available).
496 SetNotBlacklistedMessage(kAccountCreationFormHTML);
498 // Need to focus another field first for verification to work.
499 ExpectPasswordGenerationAvailable("second_password", false);
500 ExpectPasswordGenerationAvailable("first_password", true);
503 // Losing focus should not trigger a password generation popup.
504 TEST_F(PasswordGenerationAgentTest, BlurTest) {
505 LoadHTMLWithUserGesture(kDisabledElementAccountCreationFormHTML);
506 SetNotBlacklistedMessage(kDisabledElementAccountCreationFormHTML);
507 SetAccountCreationFormsDetectedMessage(0);
509 // Focus on the first password field: password generation popup should show
510 // up.
511 ExpectPasswordGenerationAvailable("first_password", true);
513 // Remove focus from everywhere by clicking an unfocusable element: password
514 // generation popup should not show up.
515 EXPECT_TRUE(SimulateElementClick("disabled"));
516 EXPECT_EQ(0u, password_generation_->messages().size());
519 } // namespace autofill