Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(423)

Side by Side Diff: chrome/renderer/autofill/password_autofill_agent_browsertest.cc

Issue 166043006: Add password manager autocomplete suggestion when a username element in clicked. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Updated so first-click suggestions on password fields only shown if already autofilled Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/strings/string_util.h" 5 #include "base/strings/string_util.h"
6 #include "base/strings/utf_string_conversions.h" 6 #include "base/strings/utf_string_conversions.h"
7 #include "chrome/test/base/chrome_render_view_test.h" 7 #include "chrome/test/base/chrome_render_view_test.h"
8 #include "components/autofill/content/common/autofill_messages.h" 8 #include "components/autofill/content/common/autofill_messages.h"
9 #include "components/autofill/content/renderer/autofill_agent.h" 9 #include "components/autofill/content/renderer/autofill_agent.h"
10 #include "components/autofill/content/renderer/form_autofill_util.h" 10 #include "components/autofill/content/renderer/form_autofill_util.h"
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 void ClearUsernameAndPasswordFields() { 247 void ClearUsernameAndPasswordFields() {
248 username_element_.setValue(""); 248 username_element_.setValue("");
249 username_element_.setAutofilled(false); 249 username_element_.setAutofilled(false);
250 password_element_.setValue(""); 250 password_element_.setValue("");
251 password_element_.setAutofilled(false); 251 password_element_.setAutofilled(false);
252 } 252 }
253 253
254 void SimulateUsernameChangeForElement(const std::string& username, 254 void SimulateUsernameChangeForElement(const std::string& username,
255 bool move_caret_to_end, 255 bool move_caret_to_end,
256 WebFrame* input_frame, 256 WebFrame* input_frame,
257 WebInputElement& username_input) { 257 WebInputElement& username_input,
258 username_input.setValue(WebString::fromUTF8(username)); 258 bool is_user_input) {
259 username_input.setValue(WebString::fromUTF8(username), is_user_input);
259 // The field must have focus or AutofillAgent will think the 260 // The field must have focus or AutofillAgent will think the
260 // change should be ignored. 261 // change should be ignored.
261 while (!username_input.focused()) 262 while (!username_input.focused())
262 input_frame->document().frame()->view()->advanceFocus(false); 263 input_frame->document().frame()->view()->advanceFocus(false);
263 if (move_caret_to_end) 264 if (move_caret_to_end)
264 username_input.setSelectionRange(username.length(), username.length()); 265 username_input.setSelectionRange(username.length(), username.length());
266 if (is_user_input)
267 password_autofill_->set_user_gesture_occurred(true);
265 autofill_agent_->textFieldDidChange(username_input); 268 autofill_agent_->textFieldDidChange(username_input);
266 // Processing is delayed because of a Blink bug: 269 // Processing is delayed because of a Blink bug:
267 // https://bugs.webkit.org/show_bug.cgi?id=16976 270 // https://bugs.webkit.org/show_bug.cgi?id=16976
268 // See PasswordAutofillAgent::TextDidChangeInTextField() for details. 271 // See PasswordAutofillAgent::TextDidChangeInTextField() for details.
269 272
270 // Autocomplete will trigger a style recalculation when we put up the next 273 // Autocomplete will trigger a style recalculation when we put up the next
271 // frame, but we don't want to wait that long. Instead, trigger a style 274 // frame, but we don't want to wait that long. Instead, trigger a style
272 // recalcuation manually after TextFieldDidChangeImpl runs. 275 // recalcuation manually after TextFieldDidChangeImpl runs.
273 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( 276 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
274 &PasswordAutofillAgentTest::LayoutMainFrame, base::Unretained(this))); 277 &PasswordAutofillAgentTest::LayoutMainFrame, base::Unretained(this)));
275 278
276 base::MessageLoop::current()->RunUntilIdle(); 279 base::MessageLoop::current()->RunUntilIdle();
277 } 280 }
278 281
282 void SimulateSuggestionChoice(const std::string& username,
283 WebInputElement& username_input) {
284 blink::WebString blink_username = blink::WebString::fromUTF8(username);
285
286 // This call is necessary to setup the autofill agent appropriate for the
287 // user selection; simulates the menu actually popping up.
288 autofill_agent_->InputElementClicked(username_input, false, true);
289
290 autofill_agent_->OnAcceptPasswordAutofillSuggestion(blink_username);
291 }
292
279 void LayoutMainFrame() { 293 void LayoutMainFrame() {
280 GetMainFrame()->view()->layout(); 294 GetMainFrame()->view()->layout();
281 } 295 }
282 296
283 void SimulateUsernameChange(const std::string& username, 297 void SimulateUsernameChange(const std::string& username,
284 bool move_caret_to_end) { 298 bool move_caret_to_end,
285 SimulateUsernameChangeForElement(username, move_caret_to_end, 299 bool is_user_input = false) {
286 GetMainFrame(), username_element_); 300 SimulateUsernameChangeForElement(username,
301 move_caret_to_end,
302 GetMainFrame(),
303 username_element_,
304 is_user_input);
287 } 305 }
288 306
289 // Tests that no suggestion popup is generated when the username_element_ is 307 // Tests that no suggestion popup is generated when the username_element_ is
290 // edited. 308 // edited.
291 void ExpectNoSuggestionsPopup() { 309 void ExpectNoSuggestionsPopup() {
292 // The first test below ensures that the suggestions have been handled by 310 // The first test below ensures that the suggestions have been handled by
293 // the password_autofill_agent, even though autocomplete='off' is set. The 311 // the password_autofill_agent, even though autocomplete='off' is set. The
294 // second check ensures that, although handled, no "show suggestions" IPC to 312 // second check ensures that, although handled, no "show suggestions" IPC to
295 // the browser was generated. 313 // the browser was generated.
296 // 314 //
297 // This is interesting in the specific case of an autocomplete='off' form 315 // This is interesting in the specific case of an autocomplete='off' form
298 // that also has a remembered username and password 316 // that also has a remembered username and password
299 // (http://crbug.com/326679). To fix the DCHECK that this case used to hit, 317 // (http://crbug.com/326679). To fix the DCHECK that this case used to hit,
300 // |true| is returned from ShowSuggestions for all forms with valid 318 // |true| is returned from ShowSuggestions for all forms with valid
301 // usersnames that are autocomplete='off', prentending that a selection box 319 // usersnames that are autocomplete='off', prentending that a selection box
302 // has been shown to the user. Of course, it hasn't, so a message is never 320 // has been shown to the user. Of course, it hasn't, so a message is never
303 // sent to the browser on acceptance, and the DCHECK isn't hit (and nothing 321 // sent to the browser on acceptance, and the DCHECK isn't hit (and nothing
304 // is filled). 322 // is filled).
305 // 323 //
306 // These tests only make sense in the context of not ignoring 324 // These tests only make sense in the context of not ignoring
307 // autocomplete='off', so only test them if the disable autocomplete='off' 325 // autocomplete='off', so only test them if the disable autocomplete='off'
308 // flag is not enabled. 326 // flag is not enabled.
309 // TODO(jww): Remove this function and callers once autocomplete='off' is 327 // TODO(jww): Remove this function and callers once autocomplete='off' is
310 // permanently ignored. 328 // permanently ignored.
311 if (!ShouldIgnoreAutocompleteOffForPasswordFields()) { 329 if (!ShouldIgnoreAutocompleteOffForPasswordFields()) {
312 EXPECT_TRUE(autofill_agent_->password_autofill_agent_->ShowSuggestions( 330 EXPECT_TRUE(autofill_agent_->password_autofill_agent_->ShowSuggestions(
313 username_element_)); 331 username_element_, false));
314 332
315 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching( 333 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
316 AutofillHostMsg_ShowPasswordSuggestions::ID)); 334 AutofillHostMsg_ShowPasswordSuggestions::ID));
317 } 335 }
318 } 336 }
319 337
320 void SimulateKeyDownEvent(const WebInputElement& element, 338 void SimulateKeyDownEvent(const WebInputElement& element,
321 ui::KeyboardCode key_code) { 339 ui::KeyboardCode key_code) {
322 blink::WebKeyboardEvent key_event; 340 blink::WebKeyboardEvent key_event;
323 key_event.windowsKeyCode = key_code; 341 key_event.windowsKeyCode = key_code;
324 autofill_agent_->textFieldDidReceiveKeyDown(element, key_event); 342 autofill_agent_->textFieldDidReceiveKeyDown(element, key_event);
325 } 343 }
326 344
327 void CheckTextFieldsStateForElements(const WebInputElement& username_element, 345 void CheckTextFieldsStateForElements(const WebInputElement& username_element,
328 const std::string& username, 346 const std::string& username,
329 bool username_autofilled, 347 bool username_autofilled,
330 const WebInputElement& password_element, 348 const WebInputElement& password_element,
331 const std::string& password, 349 const std::string& password,
332 bool password_autofilled, 350 bool password_autofilled,
333 bool checkSuggestedValue = true) { 351 bool checkSuggestedValue) {
334 EXPECT_EQ(username, 352 EXPECT_EQ(username,
335 static_cast<std::string>(username_element.value().utf8())); 353 static_cast<std::string>(username_element.value().utf8()));
336 EXPECT_EQ(username_autofilled, username_element.isAutofilled()); 354 EXPECT_EQ(username_autofilled, username_element.isAutofilled());
337 EXPECT_EQ(password, 355 EXPECT_EQ(password,
338 static_cast<std::string>( 356 static_cast<std::string>(
339 checkSuggestedValue ? password_element.suggestedValue().utf8() 357 checkSuggestedValue ? password_element.suggestedValue().utf8()
340 : password_element.value().utf8())); 358 : password_element.value().utf8()));
341 EXPECT_EQ(password_autofilled, password_element.isAutofilled()); 359 EXPECT_EQ(password_autofilled, password_element.isAutofilled());
342 } 360 }
343 361
344 // Checks the DOM-accessible value of the username element and the 362 // Checks the DOM-accessible value of the username element and the
345 // *suggested* value of the password element. 363 // *suggested* value of the password element.
346 void CheckTextFieldsState(const std::string& username, 364 void CheckTextFieldsState(const std::string& username,
347 bool username_autofilled, 365 bool username_autofilled,
348 const std::string& password, 366 const std::string& password,
349 bool password_autofilled) { 367 bool password_autofilled) {
350 CheckTextFieldsStateForElements(username_element_, username, 368 CheckTextFieldsStateForElements(username_element_,
351 username_autofilled, password_element_, 369 username,
352 password, password_autofilled); 370 username_autofilled,
371 password_element_,
372 password,
373 password_autofilled,
374 true);
353 } 375 }
354 376
355 // Checks the DOM-accessible value of the username element and the 377 // Checks the DOM-accessible value of the username element and the
356 // DOM-accessible value of the password element. 378 // DOM-accessible value of the password element.
357 void CheckTextFieldsDOMState(const std::string& username, 379 void CheckTextFieldsDOMState(const std::string& username,
358 bool username_autofilled, 380 bool username_autofilled,
359 const std::string& password, 381 const std::string& password,
360 bool password_autofilled) { 382 bool password_autofilled) {
361 CheckTextFieldsStateForElements(username_element_, 383 CheckTextFieldsStateForElements(username_element_,
362 username, 384 username,
(...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after
638 username_element_.setValue(ASCIIToUTF16(kAliceUsername)); 660 username_element_.setValue(ASCIIToUTF16(kAliceUsername));
639 autofill_agent_->textFieldDidEndEditing(username_element_); 661 autofill_agent_->textFieldDidEndEditing(username_element_);
640 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 662 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
641 } 663 }
642 664
643 // Tests that inline autocompletion works properly. 665 // Tests that inline autocompletion works properly.
644 TEST_F(PasswordAutofillAgentTest, InlineAutocomplete) { 666 TEST_F(PasswordAutofillAgentTest, InlineAutocomplete) {
645 // Simulate the browser sending back the login info. 667 // Simulate the browser sending back the login info.
646 SimulateOnFillPasswordForm(fill_data_); 668 SimulateOnFillPasswordForm(fill_data_);
647 669
648 // Clear the text fields to start fresh.
649 ClearUsernameAndPasswordFields(); 670 ClearUsernameAndPasswordFields();
650 671
651 // Simulate the user typing in the first letter of 'alice', a stored username. 672 // Simulate the user typing in the first letter of 'alice', a stored
673 // username.
652 SimulateUsernameChange("a", true); 674 SimulateUsernameChange("a", true);
653 // Both the username and password text fields should reflect selection of the 675 // Both the username and password text fields should reflect selection of the
654 // stored login. 676 // stored login.
655 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 677 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
656 // And the selection should have been set to 'lice', the last 4 letters. 678 // And the selection should have been set to 'lice', the last 4 letters.
657 CheckUsernameSelection(1, 5); 679 CheckUsernameSelection(1, 5);
658 680
659 // Now the user types the next letter of the same username, 'l'. 681 // Now the user types the next letter of the same username, 'l'.
660 SimulateUsernameChange("al", true); 682 SimulateUsernameChange("al", true);
661 // Now the fields should have the same value, but the selection should have a 683 // Now the fields should have the same value, but the selection should have a
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
815 837
816 WebElement username_element = document.getElementById(kUsernameName); 838 WebElement username_element = document.getElementById(kUsernameName);
817 WebElement password_element = document.getElementById(kPasswordName); 839 WebElement password_element = document.getElementById(kPasswordName);
818 ASSERT_FALSE(username_element.isNull()); 840 ASSERT_FALSE(username_element.isNull());
819 ASSERT_FALSE(password_element.isNull()); 841 ASSERT_FALSE(password_element.isNull());
820 842
821 WebInputElement username_input = username_element.to<WebInputElement>(); 843 WebInputElement username_input = username_element.to<WebInputElement>();
822 WebInputElement password_input = password_element.to<WebInputElement>(); 844 WebInputElement password_input = password_element.to<WebInputElement>();
823 ASSERT_FALSE(username_element.isNull()); 845 ASSERT_FALSE(username_element.isNull());
824 846
825 CheckTextFieldsStateForElements(username_input, "", false, 847 CheckTextFieldsStateForElements(
826 password_input, "", false); 848 username_input, "", false, password_input, "", false, false);
827 849
828 // Simulate the user typing in the username in the iframe, which should cause 850 // Simulate the user typing in the username in the iframe and then selecting
829 // an autofill. 851 // the autofill choice from the dropdown, thus causing an autofill.
Garrett Casto 2014/02/24 23:45:09 Just calling textFieldDidEndEditing() (or possibly
jww 2014/03/05 02:11:49 Calling textFieldDidEndEditing() is insufficient b
830 SimulateUsernameChangeForElement(kAliceUsername, true, 852 SimulateUsernameChangeForElement(
831 iframe, username_input); 853 kAliceUsername, true, iframe, username_input, false);
854 SimulateElementClick(kUsernameName);
855 SimulateSuggestionChoice(kAliceUsername, username_input);
832 856
833 CheckTextFieldsStateForElements(username_input, kAliceUsername, true, 857 CheckTextFieldsStateForElements(username_input,
834 password_input, kAlicePassword, true); 858 kAliceUsername,
859 true,
860 password_input,
861 kAlicePassword,
862 true,
863 false);
835 } 864 }
836 865
837 // Tests that a password will only be filled as a suggested and will not be 866 // Tests that a password will only be filled as a suggested and will not be
838 // accessible by the DOM until a user gesture has occurred. 867 // accessible by the DOM until a user gesture has occurred.
839 TEST_F(PasswordAutofillAgentTest, GestureRequiredTest) { 868 TEST_F(PasswordAutofillAgentTest, GestureRequiredTest) {
840 // Trigger the initial autocomplete. 869 // Trigger the initial autocomplete.
841 SimulateOnFillPasswordForm(fill_data_); 870 SimulateOnFillPasswordForm(fill_data_);
842 871
843 // The username and password should have been autocompleted. 872 // The username and password should have been autocompleted.
844 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 873 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
986 fill_data_.wait_for_username = true; 1015 fill_data_.wait_for_username = true;
987 SimulateOnFillPasswordForm(fill_data_); 1016 SimulateOnFillPasswordForm(fill_data_);
988 1017
989 // The username and password should not yet have been autocompleted. 1018 // The username and password should not yet have been autocompleted.
990 CheckTextFieldsState(std::string(), false, std::string(), false); 1019 CheckTextFieldsState(std::string(), false, std::string(), false);
991 1020
992 // Simulate a click just to force a user gesture, since the username value is 1021 // Simulate a click just to force a user gesture, since the username value is
993 // set directly. 1022 // set directly.
994 SimulateElementClick(kUsernameName); 1023 SimulateElementClick(kUsernameName);
995 1024
996 // Simulate the user entering her username. 1025 // Simulate the user entering her username and selecting the matching autofill
997 username_element_.setValue(ASCIIToUTF16(kAliceUsername), true); 1026 // from the dropdown.
998 autofill_agent_->textFieldDidEndEditing(username_element_); 1027 SimulateUsernameChange(kAliceUsername, true, true);
1028 SimulateSuggestionChoice(kAliceUsername, username_element_);
999 1029
1000 // The username and password should now have been autocompleted. 1030 // The username and password should now have been autocompleted.
1001 CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true); 1031 CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
1002 1032
1003 // JavaScript onChange events should have been triggered both for the username 1033 // JavaScript onChange events should have been triggered both for the username
1004 // and for the password. 1034 // and for the password.
1005 int username_onchange_called = -1; 1035 int username_onchange_called = -1;
1006 int password_onchange_called = -1; 1036 int password_onchange_called = -1;
1007 ASSERT_TRUE( 1037 ASSERT_TRUE(
1008 ExecuteJavaScriptAndReturnIntValue( 1038 ExecuteJavaScriptAndReturnIntValue(
1009 ASCIIToUTF16("usernameOnchangeCalled ? 1 : 0"), 1039 ASCIIToUTF16("usernameOnchangeCalled ? 1 : 0"),
1010 &username_onchange_called)); 1040 &username_onchange_called));
1011 EXPECT_EQ(1, username_onchange_called); 1041 EXPECT_EQ(1, username_onchange_called);
1012 ASSERT_TRUE( 1042 ASSERT_TRUE(
1013 ExecuteJavaScriptAndReturnIntValue( 1043 ExecuteJavaScriptAndReturnIntValue(
1014 ASCIIToUTF16("passwordOnchangeCalled ? 1 : 0"), 1044 ASCIIToUTF16("passwordOnchangeCalled ? 1 : 0"),
1015 &password_onchange_called)); 1045 &password_onchange_called));
1016 EXPECT_EQ(1, password_onchange_called); 1046 EXPECT_EQ(1, password_onchange_called);
1017 } 1047 }
1018 1048
1049 // Tests that autocomplete works when the user clicks on an element and chooses
1050 // a suggestion from the autofill suggestion list. {
Garrett Casto 2014/02/24 23:45:09 1) I think that this test is trying to verify that
jww 2014/03/05 02:11:49 I've update the comment to be more specific, but d
1051 TEST_F(PasswordAutofillAgentTest, ClickAndSelect) {
1052 // Simulate the browser sending back the login info.
1053 SimulateOnFillPasswordForm(fill_data_);
1054
1055 // Clear the text fields to start fresh.
1056 ClearUsernameAndPasswordFields();
1057
1058 SimulateElementClick(kUsernameName);
Garrett Casto 2014/02/24 23:45:09 Is this necessary to focus the element? As it's cu
jww 2014/03/05 02:11:49 Yeah, it's a bit odd because the SimulateElementCl
1059 SimulateSuggestionChoice(kAliceUsername, username_element_);
1060
1061 CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
1062 }
1063
1019 } // namespace autofill 1064 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698