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

Unified Diff: components/autofill/content/renderer/password_autofill_agent.cc

Issue 492043003: Fill on account select in the password manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase on ToT Created 6 years, 2 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 side-by-side diff with in-line comments
Download patch
Index: components/autofill/content/renderer/password_autofill_agent.cc
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index f2882c9ff03c8f317012bc560fe544ab5dbe9f69..ac534c0f7d6dc04fb9f98e75bf2bf6793b8b2eae 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -5,6 +5,7 @@
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
@@ -13,6 +14,7 @@
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
+#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_autofill_util.h"
#include "components/autofill/core/common/password_form.h"
@@ -39,6 +41,12 @@
namespace autofill {
namespace {
+enum FillUserNameAndPasswordOptions {
+ EXACT_USERNAME_MATCH = 1 << 0,
+ SET_SELECTION = 1 << 1,
+ FILL_PREFERRED_USERNAME = 1 << 2
+};
+
// The size above which we stop triggering autocomplete.
static const size_t kMaximumTextSizeForAutocomplete = 1000;
@@ -62,6 +70,23 @@ struct FormElements {
typedef std::vector<FormElements*> FormElementsList;
+bool ShouldFillOnAccountSelect() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableFillOnAccountSelect)) {
+ return true;
+ }
+
+ // This is made explicit in anticiption of experimental groups being added.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableFillOnAccountSelect)) {
+ return false;
+ }
+
+ // TODO(jww): Add experimental groups check here.
+
+ return false;
+}
+
// Helper to search the given form element for the specified input elements
// in |data|, and add results to |result|.
static bool FindFormInputElements(blink::WebFormElement* fe,
@@ -281,14 +306,16 @@ bool GetSuggestionsStats(const PasswordFormFillData& fill_data,
// with values from |fill_data|. The |password_element| will only have the
// |suggestedValue| set, and will be registered for copying that to the real
// value through |registration_callback|. The function returns true when
-// selected username comes from |fill_data.other_possible_usernames|.
+// selected username comes from |fill_data.other_possible_usernames|. |options|
+// should be a bitwise mask of FillUserNameAndPasswordOptions values.
bool FillUserNameAndPassword(
blink::WebInputElement* username_element,
blink::WebInputElement* password_element,
const PasswordFormFillData& fill_data,
- bool exact_username_match,
- bool set_selection,
+ const int options,
base::Callback<void(blink::WebInputElement*)> registration_callback) {
+ bool exact_username_match = (options & EXACT_USERNAME_MATCH) != 0;
+ bool set_selection = (options & SET_SELECTION) != 0;
bool other_possible_username_selected = false;
// Don't fill username if password can't be set.
if (!IsElementAutocompletable(*password_element))
@@ -383,7 +410,7 @@ bool FillUserNameAndPassword(
// |suggestedValue| set, and |suggestedValue| will be registered for copying to
// the real value through |registration_callback|. Returns true when the
// username gets selected from |other_possible_usernames|, else returns false.
-bool FillFormOnPasswordRecieved(
+bool FillFormOnPasswordReceived(
const PasswordFormFillData& fill_data,
blink::WebInputElement username_element,
blink::WebInputElement password_element,
@@ -402,23 +429,36 @@ bool FillFormOnPasswordRecieved(
if (!IsElementAutocompletable(password_element))
return false;
- // Try to set the username to the preferred name, but only if the field
- // can be set and isn't prefilled.
- if (form_contains_username_field &&
- IsElementAutocompletable(username_element) &&
- username_element.value().isEmpty()) {
- // TODO(tkent): Check maxlength and pattern.
- username_element.setValue(fill_data.basic_data.fields[0].value, true);
+ // Try to set the username to the preferred name, but only if:
+ // (a) The username element is autocompletable, and
+ // (b) The fill-on-account-select flag is not set, and
+ // (b) The username element isn't prefilled
Garrett Casto 2014/11/04 23:35:30 Is that last line supposed to be (c)?
jww 2014/11/06 21:28:56 Yup :-)
+ //
+ // If (a) is true, but (b) is false, then just mark the username element as
+ // autofilled and return so the fill step is skipped.
+ //
+ // If (a) is false, but (b) is true, then the username element should not be
+ // autofilled, but the user should still be able to select to fill the
+ // password element, so the password element must be marked as autofilled and
+ // the fill step should also be skipped.
+ if (IsElementAutocompletable(username_element)) {
Garrett Casto 2014/11/04 23:35:30 I think that you dropped the form_contains_usernam
jww 2014/11/06 21:28:56 Done.
Garrett Casto 2014/11/23 07:49:48 Can you add a test so that this would have been ca
jww 2014/11/25 02:46:25 Yup, unit tests are my next step.
+ if (ShouldFillOnAccountSelect()) {
+ username_element.setAutofilled(true);
+ return false;
+ } else if (username_element.value().isEmpty()) {
+ // TODO(tkent): Check maxlength and pattern.
+ username_element.setValue(fill_data.basic_data.fields[0].value, true);
+ }
+ } else if (ShouldFillOnAccountSelect()) {
+ password_element.setAutofilled(true);
+ return false;
}
// Fill if we have an exact match for the username. Note that this sets
// username to autofilled.
- return FillUserNameAndPassword(&username_element,
- &password_element,
- fill_data,
- true /* exact_username_match */,
- false /* set_selection */,
- registration_callback);
+ return FillUserNameAndPassword(
+ &username_element, &password_element, fill_data,
+ EXACT_USERNAME_MATCH | FILL_PREFERRED_USERNAME, registration_callback);
}
// Takes a |map| with pointers as keys and linked_ptr as values, and returns
@@ -520,11 +560,7 @@ bool PasswordAutofillAgent::TextFieldDidEndEditing(
// Do not set selection when ending an editing session, otherwise it can
// mess with focus.
if (FillUserNameAndPassword(
- &username,
- &password,
- fill_data,
- true /* exact_username_match */,
- false /* set_selection */,
+ &username, &password, fill_data, EXACT_USERNAME_MATCH,
base::Bind(&PasswordValueGatekeeper::RegisterElement,
base::Unretained(&gatekeeper_)))) {
usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
@@ -684,11 +720,28 @@ bool PasswordAutofillAgent::DidClearAutofillSelection(
return true;
}
+PasswordAutofillAgent::LoginToPasswordInfoMap::iterator
+PasswordAutofillAgent::FindPasswordInfoForElement(
+ const blink::WebInputElement& element) {
+ const blink::WebInputElement* username_element;
Garrett Casto 2014/11/04 23:35:30 Why does this need to be a pointer?
jww 2014/11/06 21:28:56 I was trying to have only return statement, and si
+ if (!element.isPasswordField()) {
+ username_element = &element;
+ } else {
+ PasswordToLoginMap::const_iterator password_iter =
+ password_to_username_.find(element);
Garrett Casto 2014/11/04 23:35:30 Where is this defined and populated?
jww 2014/11/06 21:28:57 It's defined in password_autofill_agent.h and it i
+ if (password_iter == password_to_username_.end())
+ return login_to_password_info_.end();
+ username_element = &password_iter->second;
+ }
+
+ return login_to_password_info_.find(*username_element);
+}
+
bool PasswordAutofillAgent::ShowSuggestions(
const blink::WebInputElement& element,
bool show_all) {
LoginToPasswordInfoMap::const_iterator iter =
- login_to_password_info_.find(element);
+ FindPasswordInfoForElement(element);
if (iter == login_to_password_info_.end())
return false;
@@ -700,7 +753,13 @@ bool PasswordAutofillAgent::ShowSuggestions(
!IsElementAutocompletable(iter->second.password_field))
return true;
- return ShowSuggestionPopup(iter->second.fill_data, element, show_all);
+ // Chrome should never show more than one account for a password element since
+ // this implies that the username element cannot be modified. Thus even if
+ // |show_all| is true, check if the element in question is a password element
+ // for the call to ShowSuggestionPopup.
+ return ShowSuggestionPopup(iter->second.fill_data, iter->first,
+ show_all && !element.isPasswordField(),
+ element.isPasswordField());
Garrett Casto 2014/11/04 23:35:30 Is there any reason why you need the fourth parame
jww 2014/11/06 21:28:57 Yes, because we ultimately need the username as th
Garrett Casto 2014/11/23 07:49:48 I'm not sure that I understand why we need the use
jww 2014/11/25 02:46:25 I believe it's because the browser process can onl
}
bool PasswordAutofillAgent::OriginCanAccessPasswordManager(
@@ -1049,10 +1108,8 @@ void PasswordAutofillAgent::OnFillPasswordForm(
// If wait_for_username is true, we don't want to initially fill the form
// until the user types in a valid username.
if (!form_data.wait_for_username &&
- FillFormOnPasswordRecieved(
- form_data,
- username_element,
- password_element,
+ FillFormOnPasswordReceived(
+ form_data, username_element, password_element,
base::Bind(&PasswordValueGatekeeper::RegisterElement,
base::Unretained(&gatekeeper_)))) {
usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
@@ -1095,7 +1152,8 @@ PasswordAutofillAgent::PasswordInfo::PasswordInfo()
bool PasswordAutofillAgent::ShowSuggestionPopup(
const PasswordFormFillData& fill_data,
const blink::WebInputElement& user_input,
- bool show_all) {
+ bool show_all,
+ bool show_on_password_field) {
blink::WebFrame* frame = user_input.document().frame();
if (!frame)
return false;
@@ -1110,6 +1168,12 @@ bool PasswordAutofillAgent::ShowSuggestionPopup(
user_input, &form, &field, REQUIRE_NONE);
blink::WebInputElement selected_element = user_input;
+ if (show_on_password_field) {
+ LoginToPasswordInfoMap::const_iterator iter =
+ login_to_password_info_.find(user_input);
+ DCHECK(iter != login_to_password_info_.end());
+ selected_element = iter->second.password_field;
+ }
gfx::Rect bounding_box(selected_element.boundsInViewportSpace());
float scale = web_view_->pageScaleFactor();
@@ -1117,8 +1181,15 @@ bool PasswordAutofillAgent::ShowSuggestionPopup(
bounding_box.y() * scale,
bounding_box.width() * scale,
bounding_box.height() * scale);
- Send(new AutofillHostMsg_ShowPasswordSuggestions(
- routing_id(), field, user_input.value(), show_all, bounding_box_scaled));
+ if (show_on_password_field) {
+ Send(new AutofillHostMsg_ShowPasswordSuggestionsForPasswordField(
+ routing_id(), field, user_input.value(), show_all,
+ bounding_box_scaled));
+ } else {
+ Send(new AutofillHostMsg_ShowPasswordSuggestions(
+ routing_id(), field, user_input.value(), show_all,
+ bounding_box_scaled));
+ }
bool suggestions_present = false;
if (GetSuggestionsStats(fill_data, user_input.value(), show_all,
@@ -1152,11 +1223,7 @@ void PasswordAutofillAgent::PerformInlineAutocomplete(
// Fill the user and password field with the most relevant match. Android
// only fills in the fields after the user clicks on the suggestion popup.
if (FillUserNameAndPassword(
- &username,
- &password,
- fill_data,
- false /* exact_username_match */,
- true /* set_selection */,
+ &username, &password, fill_data, SET_SELECTION,
base::Bind(&PasswordValueGatekeeper::RegisterElement,
base::Unretained(&gatekeeper_)))) {
usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
@@ -1197,7 +1264,7 @@ bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
return false;
blink::WebInputElement input = element.to<blink::WebInputElement>();
- LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
+ LoginToPasswordInfoMap::iterator iter = FindPasswordInfoForElement(input);
if (iter == login_to_password_info_.end())
return false;

Powered by Google App Engine
This is Rietveld 408576698