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 "components/autofill/content/renderer/password_form_conversion_utils.h"
9 #include "base/i18n/case_conversion.h"
10 #include "base/lazy_instance.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string_util.h"
14 #include "components/autofill/content/renderer/form_autofill_util.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/autofill/core/common/password_form_field_prediction_map.h"
17 #include "google_apis/gaia/gaia_urls.h"
18 #include "third_party/WebKit/public/platform/WebString.h"
19 #include "third_party/WebKit/public/platform/WebVector.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebFormControlElement.h"
22 #include "third_party/WebKit/public/web/WebFrame.h"
23 #include "third_party/WebKit/public/web/WebInputElement.h"
24 #include "third_party/icu/source/i18n/unicode/regex.h"
26 using blink::WebDocument
;
27 using blink::WebFormControlElement
;
28 using blink::WebFormElement
;
29 using blink::WebFrame
;
30 using blink::WebInputElement
;
31 using blink::WebString
;
32 using blink::WebVector
;
37 // PasswordForms can be constructed for both WebFormElements and for collections
38 // of WebInputElements that are not in a WebFormElement. This intermediate
39 // aggregating structure is provided so GetPasswordForm() only has one
40 // view of the underlying data, regardless of its origin.
41 struct SyntheticForm
{
45 std::vector
<blink::WebElement
> fieldsets
;
46 std::vector
<blink::WebFormControlElement
> control_elements
;
47 blink::WebDocument document
;
48 blink::WebString action
;
51 DISALLOW_COPY_AND_ASSIGN(SyntheticForm
);
54 SyntheticForm::SyntheticForm() {}
55 SyntheticForm::~SyntheticForm() {}
57 // Layout classification of password forms
58 // A layout sequence of a form is the sequence of it's non-password and password
59 // input fields, represented by "N" and "P", respectively. A form like this
61 // <input type='text' ...>
62 // <input type='hidden' ...>
63 // <input type='password' ...>
64 // <input type='submit' ...>
66 // has the layout sequence "NP" -- "N" for the first field, and "P" for the
67 // third. The second and fourth fields are ignored, because they are not text
70 // The code below classifies the layout (see PasswordForm::Layout) of a form
71 // based on its layout sequence. This is done by assigning layouts regular
72 // expressions over the alphabet {N, P}. LAYOUT_OTHER is implicitly the type
73 // corresponding to all layout sequences not matching any other layout.
75 // LAYOUT_LOGIN_AND_SIGNUP is classified by NPN+P.*. This corresponds to a form
76 // which starts with a login section (NP) and continues with a sign-up section
77 // (N+P.*). The aim is to distinguish such forms from change password-forms
78 // (N*PPP?.*) and forms which use password fields to store private but
79 // non-password data (could look like, e.g., PN+P.*).
80 const char kLoginAndSignupRegex
[] =
81 "NP" // Login section.
82 "N+P" // Sign-up section.
83 ".*"; // Anything beyond that.
85 const char kAutocompleteUsername
[] = "username";
86 const char kAutocompleteCurrentPassword
[] = "current-password";
87 const char kAutocompleteNewPassword
[] = "new-password";
89 icu::RegexMatcher
* CreateMatcher(
90 void* instance
, const char* pattern
) {
91 const icu::UnicodeString
icu_pattern(pattern
);
93 UErrorCode status
= U_ZERO_ERROR
;
94 // Use placement new to initialize the instance in the preallocated space.
95 // The "(instance)" is very important to force POD type initialization.
96 icu::RegexMatcher
* matcher
= new (instance
) icu::RegexMatcher(
97 icu_pattern
, UREGEX_CASE_INSENSITIVE
, status
);
98 DCHECK(U_SUCCESS(status
));
102 struct LoginAndSignupLazyInstanceTraits
103 : public base::DefaultLazyInstanceTraits
<icu::RegexMatcher
> {
104 static icu::RegexMatcher
* New(void* instance
) {
105 return CreateMatcher(instance
, kLoginAndSignupRegex
);
109 bool Matches(icu::RegexMatcher
* matcher
, base::StringPiece expression
) {
110 icu::UnicodeString
icu_input(icu::UnicodeString::fromUTF8(
111 icu::StringPiece(expression
.data(), expression
.length())));
112 matcher
->reset(icu_input
);
114 UErrorCode status
= U_ZERO_ERROR
;
115 UBool match
= matcher
->find(0, status
);
116 DCHECK(U_SUCCESS(status
));
117 return match
== TRUE
;
120 base::LazyInstance
<icu::RegexMatcher
, LoginAndSignupLazyInstanceTraits
>
121 login_and_signup_matcher
= LAZY_INSTANCE_INITIALIZER
;
123 // Given the sequence of non-password and password text input fields of a form,
124 // represented as a string of Ns (non-password) and Ps (password), computes the
125 // layout type of that form.
126 PasswordForm::Layout
SequenceToLayout(base::StringPiece layout_sequence
) {
127 if (Matches(login_and_signup_matcher
.Pointer(), layout_sequence
))
128 return PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP
;
129 return PasswordForm::Layout::LAYOUT_OTHER
;
132 void PopulateSyntheticFormFromWebForm(const WebFormElement
& web_form
,
133 SyntheticForm
* synthetic_form
) {
134 synthetic_form
->control_elements
= ExtractAutofillableElementsInForm(
136 synthetic_form
->action
= web_form
.action();
137 synthetic_form
->document
= web_form
.document();
140 // Checks in a case-insensitive way if the autocomplete attribute for the given
141 // |element| is present and has the specified |value_in_lowercase|.
142 bool HasAutocompleteAttributeValue(const WebInputElement
& element
,
143 const char* value_in_lowercase
) {
144 return base::LowerCaseEqualsASCII(
145 base::StringPiece16(element
.getAttribute("autocomplete")),
149 // Helper to determine which password is the main (current) one, and which is
150 // the new password (e.g., on a sign-up or change password form), if any.
151 bool LocateSpecificPasswords(std::vector
<WebInputElement
> passwords
,
152 WebInputElement
* current_password
,
153 WebInputElement
* new_password
) {
154 DCHECK(current_password
&& current_password
->isNull());
155 DCHECK(new_password
&& new_password
->isNull());
157 // First, look for elements marked with either autocomplete='current-password'
158 // or 'new-password' -- if we find any, take the hint, and treat the first of
159 // each kind as the element we are looking for.
160 for (std::vector
<WebInputElement
>::const_iterator it
= passwords
.begin();
161 it
!= passwords
.end(); it
++) {
162 if (HasAutocompleteAttributeValue(*it
, kAutocompleteCurrentPassword
) &&
163 current_password
->isNull()) {
164 *current_password
= *it
;
165 } else if (HasAutocompleteAttributeValue(*it
, kAutocompleteNewPassword
) &&
166 new_password
->isNull()) {
171 // If we have seen an element with either of autocomplete attributes above,
172 // take that as a signal that the page author must have intentionally left the
173 // rest of the password fields unmarked. Perhaps they are used for other
174 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we
175 // normally do, and ignore the rest of the password fields.
176 if (!current_password
->isNull() || !new_password
->isNull())
179 if (passwords
.empty())
182 switch (passwords
.size()) {
184 // Single password, easy.
185 *current_password
= passwords
[0];
188 if (!passwords
[0].value().isEmpty() &&
189 passwords
[0].value() == passwords
[1].value()) {
190 // Two identical non-empty passwords: assume we are seeing a new
191 // password with a confirmation. This can be either a sign-up form or a
192 // password change form that does not ask for the old password.
193 *new_password
= passwords
[0];
195 // Assume first is old password, second is new (no choice but to guess).
196 // This case also includes empty passwords in order to allow filling of
197 // password change forms (that also could autofill for sign up form, but
198 // we can't do anything with this using only client side information).
199 *current_password
= passwords
[0];
200 *new_password
= passwords
[1];
204 if (!passwords
[0].value().isEmpty() &&
205 passwords
[0].value() == passwords
[1].value() &&
206 passwords
[0].value() == passwords
[2].value()) {
207 // All three passwords are the same and non-empty? This does not make
208 // any sense, give up.
210 } else if (passwords
[1].value() == passwords
[2].value()) {
211 // New password is the duplicated one, and comes second; or empty form
212 // with 3 password fields, in which case we will assume this layout.
213 *current_password
= passwords
[0];
214 *new_password
= passwords
[1];
215 } else if (passwords
[0].value() == passwords
[1].value()) {
216 // It is strange that the new password comes first, but trust more which
217 // fields are duplicated than the ordering of fields. Assume that
218 // any password fields after the new password contain sensitive
219 // information that isn't actually a password (security hint, SSN, etc.)
220 *new_password
= passwords
[0];
222 // Three different passwords, or first and last match with middle
223 // different. No idea which is which, so no luck.
230 // Checks the |form_predictions| map to see if there is a key associated with
231 // the |prediction_type| value. Assigns the key to |prediction_field| and
232 // returns true if it is found.
233 bool MapContainsPrediction(
234 const std::map
<WebInputElement
, PasswordFormFieldPredictionType
>&
236 PasswordFormFieldPredictionType prediction_type
,
237 WebInputElement
* prediction_field
) {
238 for (auto it
= form_predictions
.begin(); it
!= form_predictions
.end(); ++it
) {
239 if (it
->second
== prediction_type
) {
240 (*prediction_field
) = it
->first
;
247 void FindPredictedElements(
248 const SyntheticForm
& form
,
249 const FormData
& form_data
,
250 const FormsPredictionsMap
& form_predictions
,
251 std::map
<WebInputElement
, PasswordFormFieldPredictionType
>*
252 predicted_elements
) {
253 // Matching only requires that action and name of the form match to allow
254 // the username to be updated even if the form is changed after page load.
255 // See https://crbug.com/476092 for more details.
256 auto predictions_iterator
= form_predictions
.begin();
257 for (;predictions_iterator
!= form_predictions
.end();
258 ++predictions_iterator
) {
259 if (predictions_iterator
->first
.action
== form_data
.action
&&
260 predictions_iterator
->first
.name
== form_data
.name
) {
265 if (predictions_iterator
== form_predictions
.end())
268 std::vector
<blink::WebFormControlElement
> autofillable_elements
=
269 ExtractAutofillableElementsFromSet(form
.control_elements
);
271 const PasswordFormFieldPredictionMap
& field_predictions
=
272 predictions_iterator
->second
;
273 for (PasswordFormFieldPredictionMap::const_iterator prediction
=
274 field_predictions
.begin();
275 prediction
!= field_predictions
.end(); ++prediction
) {
276 const FormFieldData
& target_field
= prediction
->first
;
277 const PasswordFormFieldPredictionType
& type
= prediction
->second
;
279 for (size_t i
= 0; i
< form
.control_elements
.size(); ++i
) {
280 if (form
.control_elements
[i
].nameForAutofill() == target_field
.name
) {
281 const WebInputElement
* input_element
=
282 toWebInputElement(&form
.control_elements
[i
]);
283 // TODO(sebsg): Investigate why this guard is necessary, see
284 // https://crbug.com/517490 for more details.
286 (*predicted_elements
)[*input_element
] = type
;
294 // TODO(msramek): Move the reauthentication recognition code to the browser.
295 const char kPasswordSiteUrlRegex
[] =
296 "passwords(?:-[a-z-]+\\.corp)?\\.google\\.com";
298 struct PasswordSiteUrlLazyInstanceTraits
299 : public base::DefaultLazyInstanceTraits
<icu::RegexMatcher
> {
300 static icu::RegexMatcher
* New(void* instance
) {
301 return CreateMatcher(instance
, kPasswordSiteUrlRegex
);
305 base::LazyInstance
<icu::RegexMatcher
, PasswordSiteUrlLazyInstanceTraits
>
306 password_site_matcher
= LAZY_INSTANCE_INITIALIZER
;
308 // Get information about a login form encapsulated in a PasswordForm struct.
309 // If an element of |form| has an entry in |nonscript_modified_values|, the
310 // associated string is used instead of the element's value to create
312 bool GetPasswordForm(const SyntheticForm
& form
,
313 PasswordForm
* password_form
,
314 const ModifiedValues
* nonscript_modified_values
,
315 const FormsPredictionsMap
* form_predictions
) {
316 WebInputElement latest_input_element
;
317 WebInputElement username_element
;
318 password_form
->username_marked_by_site
= false;
319 std::vector
<WebInputElement
> passwords
;
320 std::vector
<base::string16
> other_possible_usernames
;
322 // Bail if this is a GAIA passwords site reauthentication form, so that
323 // the form will be ignored.
324 // TODO(msramek): Move this logic to the browser, and disable filling only
325 // for the sync credential and if passwords are being synced.
326 if (IsGaiaReauthenticationForm(
327 GURL(form
.document
.url()).GetOrigin(), form
.control_elements
)) {
331 std::map
<WebInputElement
, PasswordFormFieldPredictionType
> predicted_elements
;
332 if (form_predictions
) {
333 FindPredictedElements(form
, password_form
->form_data
, *form_predictions
,
334 &predicted_elements
);
337 std::string layout_sequence
;
338 layout_sequence
.reserve(form
.control_elements
.size());
339 for (size_t i
= 0; i
< form
.control_elements
.size(); ++i
) {
340 WebFormControlElement control_element
= form
.control_elements
[i
];
342 WebInputElement
* input_element
= toWebInputElement(&control_element
);
343 if (!input_element
|| !input_element
->isEnabled())
346 if (input_element
->isTextField()) {
347 if (input_element
->isPasswordField())
348 layout_sequence
.push_back('P');
350 layout_sequence
.push_back('N');
353 bool password_marked_by_autocomplete_attribute
=
354 HasAutocompleteAttributeValue(*input_element
,
355 kAutocompleteCurrentPassword
) ||
356 HasAutocompleteAttributeValue(*input_element
, kAutocompleteNewPassword
);
358 // If the password field is readonly, the page is likely using a virtual
359 // keyboard and bypassing the password field value (see
360 // http://crbug.com/475488). There is nothing Chrome can do to fill
361 // passwords for now. Continue processing in case when the password field
362 // was made readonly by JavaScript before submission. We can do this by
363 // checking whether password element was updated not from JavaScript.
364 if (input_element
->isPasswordField() &&
365 (!input_element
->isReadOnly() ||
366 (nonscript_modified_values
&&
367 nonscript_modified_values
->find(*input_element
) !=
368 nonscript_modified_values
->end()) ||
369 password_marked_by_autocomplete_attribute
)) {
370 // We add the field to the list of password fields if it was not flagged
371 // as a special NOT_PASSWORD prediction by Autofill. The NOT_PASSWORD
372 // mechanism exists because some webpages use the type "password" for
373 // fields which Autofill knows shouldn't be treated as passwords by the
374 // Password Manager. This is ultimately bypassed if the field has
375 // autocomplete attributes.
376 auto possible_password_element_iterator
=
377 predicted_elements
.find(*input_element
);
378 if (password_marked_by_autocomplete_attribute
||
379 possible_password_element_iterator
== predicted_elements
.end() ||
380 possible_password_element_iterator
->second
!=
381 PREDICTION_NOT_PASSWORD
) {
382 passwords
.push_back(*input_element
);
384 // If we have not yet considered any element to be the username so far,
385 // provisionally select the input element just before the first password
386 // element to be the username. This choice will be overruled if we later
387 // find an element with autocomplete='username'.
388 if (username_element
.isNull() && !latest_input_element
.isNull()) {
389 username_element
= latest_input_element
;
390 // Remove the selected username from other_possible_usernames.
391 if (!latest_input_element
.value().isEmpty()) {
392 DCHECK(!other_possible_usernames
.empty());
393 DCHECK_EQ(base::string16(latest_input_element
.value()),
394 other_possible_usernames
.back());
395 other_possible_usernames
.pop_back();
400 // Various input types such as text, url, email can be a username field.
401 if (input_element
->isTextField() && !input_element
->isPasswordField()) {
402 if (HasAutocompleteAttributeValue(*input_element
,
403 kAutocompleteUsername
)) {
404 if (password_form
->username_marked_by_site
) {
405 // A second or subsequent element marked with autocomplete='username'.
406 // This makes us less confident that we have understood the form. We
407 // will stick to our choice that the first such element was the real
408 // username, but will start collecting other_possible_usernames from
409 // the extra elements marked with autocomplete='username'. Note that
410 // unlike username_element, other_possible_usernames is used only for
411 // autofill, not for form identification, and blank autofill entries
412 // are not useful, so we do not collect empty strings.
413 if (!input_element
->value().isEmpty())
414 other_possible_usernames
.push_back(input_element
->value());
416 // The first element marked with autocomplete='username'. Take the
417 // hint and treat it as the username (overruling the tentative choice
418 // we might have made before). Furthermore, drop all other possible
419 // usernames we have accrued so far: they come from fields not marked
420 // with the autocomplete attribute, making them unlikely alternatives.
421 username_element
= *input_element
;
422 password_form
->username_marked_by_site
= true;
423 other_possible_usernames
.clear();
426 if (password_form
->username_marked_by_site
) {
427 // Having seen elements with autocomplete='username', elements without
428 // this attribute are no longer interesting. No-op.
430 // No elements marked with autocomplete='username' so far whatsoever.
431 // If we have not yet selected a username element even provisionally,
432 // then remember this element for the case when the next field turns
433 // out to be a password. Save a non-empty username as a possible
434 // alternative, at least for now.
435 if (username_element
.isNull())
436 latest_input_element
= *input_element
;
437 if (!input_element
->value().isEmpty())
438 other_possible_usernames
.push_back(input_element
->value());
443 password_form
->layout
= SequenceToLayout(layout_sequence
);
445 WebInputElement predicted_username_element
;
446 bool map_has_username_prediction
= MapContainsPrediction(
447 predicted_elements
, PREDICTION_USERNAME
, &predicted_username_element
);
449 // Let server predictions override the selection of the username field. This
450 // allows instant adjusting without changing Chromium code.
451 auto username_element_iterator
= predicted_elements
.find(username_element
);
452 if (map_has_username_prediction
&&
453 (username_element_iterator
== predicted_elements
.end() ||
454 username_element_iterator
->second
!= PREDICTION_USERNAME
)) {
456 find(other_possible_usernames
.begin(), other_possible_usernames
.end(),
457 predicted_username_element
.value());
458 if (it
!= other_possible_usernames
.end())
459 other_possible_usernames
.erase(it
);
460 if (!username_element
.isNull()) {
461 other_possible_usernames
.push_back(username_element
.value());
463 username_element
= predicted_username_element
;
464 password_form
->was_parsed_using_autofill_predictions
= true;
467 if (!username_element
.isNull()) {
468 password_form
->username_element
= username_element
.nameForAutofill();
469 base::string16 username_value
= username_element
.value();
470 if (nonscript_modified_values
!= nullptr) {
471 auto username_iterator
=
472 nonscript_modified_values
->find(username_element
);
473 if (username_iterator
!= nonscript_modified_values
->end()) {
474 base::string16 typed_username_value
= username_iterator
->second
;
475 if (!base::StartsWith(
476 base::i18n::ToLower(username_value
),
477 base::i18n::ToLower(typed_username_value
),
478 base::CompareCase::SENSITIVE
)) {
479 // We check that |username_value| was not obtained by autofilling
480 // |typed_username_value|. In case when it was, |typed_username_value|
481 // is incomplete, so we should leave autofilled value.
482 username_value
= typed_username_value
;
486 password_form
->username_value
= username_value
;
489 WebInputElement password
;
490 WebInputElement new_password
;
491 if (!LocateSpecificPasswords(passwords
, &password
, &new_password
))
494 password_form
->origin
= GetCanonicalOriginForDocument(form
.document
);
495 GURL::Replacements rep
;
497 password_form
->signon_realm
=
498 password_form
->origin
.ReplaceComponents(rep
).spec();
499 password_form
->other_possible_usernames
.swap(other_possible_usernames
);
501 if (!password
.isNull()) {
502 password_form
->password_element
= password
.nameForAutofill();
503 blink::WebString password_value
= password
.value();
504 if (nonscript_modified_values
!= nullptr) {
505 auto password_iterator
= nonscript_modified_values
->find(password
);
506 if (password_iterator
!= nonscript_modified_values
->end())
507 password_value
= password_iterator
->second
;
509 password_form
->password_value
= password_value
;
511 if (!new_password
.isNull()) {
512 password_form
->new_password_element
= new_password
.nameForAutofill();
513 password_form
->new_password_value
= new_password
.value();
514 password_form
->new_password_value_is_default
=
515 new_password
.getAttribute("value") == new_password
.value();
516 if (HasAutocompleteAttributeValue(new_password
, kAutocompleteNewPassword
))
517 password_form
->new_password_marked_by_site
= true;
520 if (username_element
.isNull()) {
521 // To get a better idea on how password forms without a username field
522 // look like, report the total number of text and password fields.
523 UMA_HISTOGRAM_COUNTS_100(
524 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount",
525 layout_sequence
.size());
526 // For comparison, also report the number of password fields.
527 UMA_HISTOGRAM_COUNTS_100(
528 "PasswordManager.EmptyUsernames.PasswordFieldCount",
529 std::count(layout_sequence
.begin(), layout_sequence
.end(), 'P'));
532 password_form
->scheme
= PasswordForm::SCHEME_HTML
;
533 password_form
->ssl_valid
= false;
534 password_form
->preferred
= false;
535 password_form
->blacklisted_by_user
= false;
536 password_form
->type
= PasswordForm::TYPE_MANUAL
;
541 GURL
StripAuthAndParams(const GURL
& gurl
) {
542 // We want to keep the path but strip any authentication data, as well as
543 // query and ref portions of URL, for the form action and form origin.
544 GURL::Replacements rep
;
549 return gurl
.ReplaceComponents(rep
);
554 GURL
GetCanonicalActionForForm(const WebFormElement
& form
) {
555 WebString action
= form
.action();
557 action
= WebString(""); // missing 'action' attribute implies current URL
558 GURL
full_action(form
.document().completeURL(action
));
559 return StripAuthAndParams(full_action
);
562 GURL
GetCanonicalOriginForDocument(const WebDocument
& document
) {
563 GURL
full_origin(document
.url());
564 return StripAuthAndParams(full_origin
);
567 bool IsGaiaReauthenticationForm(
569 const WebVector
<blink::WebFormControlElement
>& control_elements
) {
570 if (origin
!= GaiaUrls::GetInstance()->gaia_url().GetOrigin())
573 bool has_rart_field
= false;
574 bool has_continue_field
= false;
576 for (const blink::WebFormControlElement
& element
: control_elements
) {
577 // We're only interested in the presence
578 // of <input type="hidden" /> elements.
579 CR_DEFINE_STATIC_LOCAL(WebString
, kHidden
, ("hidden"));
580 const blink::WebInputElement
* input
= blink::toWebInputElement(&element
);
581 if (!input
|| input
->formControlType() != kHidden
)
584 // There must be a hidden input named "rart".
585 if (input
->formControlName() == "rart")
586 has_rart_field
= true;
588 // There must be a hidden input named "continue", whose value points
589 // to a password (or password testing) site.
590 if (input
->formControlName() == "continue" &&
591 Matches(password_site_matcher
.Pointer(), input
->value().utf8())) {
592 has_continue_field
= true;
596 return has_rart_field
&& has_continue_field
;
599 scoped_ptr
<PasswordForm
> CreatePasswordFormFromWebForm(
600 const WebFormElement
& web_form
,
601 const ModifiedValues
* nonscript_modified_values
,
602 const FormsPredictionsMap
* form_predictions
) {
603 if (web_form
.isNull())
604 return scoped_ptr
<PasswordForm
>();
606 scoped_ptr
<PasswordForm
> password_form(new PasswordForm());
607 password_form
->action
= GetCanonicalActionForForm(web_form
);
608 if (!password_form
->action
.is_valid())
609 return scoped_ptr
<PasswordForm
>();
611 SyntheticForm synthetic_form
;
612 PopulateSyntheticFormFromWebForm(web_form
, &synthetic_form
);
614 WebFormElementToFormData(web_form
,
615 blink::WebFormControlElement(),
617 &password_form
->form_data
,
618 NULL
/* FormFieldData */);
620 if (!GetPasswordForm(synthetic_form
, password_form
.get(),
621 nonscript_modified_values
, form_predictions
))
622 return scoped_ptr
<PasswordForm
>();
624 return password_form
.Pass();
627 scoped_ptr
<PasswordForm
> CreatePasswordFormFromUnownedInputElements(
628 const WebFrame
& frame
,
629 const ModifiedValues
* nonscript_modified_values
,
630 const FormsPredictionsMap
* form_predictions
) {
631 SyntheticForm synthetic_form
;
632 synthetic_form
.control_elements
=
633 GetUnownedAutofillableFormFieldElements(frame
.document().all(),
634 &synthetic_form
.fieldsets
);
635 synthetic_form
.document
= frame
.document();
637 if (synthetic_form
.control_elements
.empty())
638 return scoped_ptr
<PasswordForm
>();
640 scoped_ptr
<PasswordForm
> password_form(new PasswordForm());
641 UnownedPasswordFormElementsAndFieldSetsToFormData(
642 synthetic_form
.fieldsets
,
643 synthetic_form
.control_elements
,
644 nullptr, frame
.document(),
646 &password_form
->form_data
,
647 nullptr /* FormFieldData */);
648 if (!GetPasswordForm(synthetic_form
, password_form
.get(),
649 nonscript_modified_values
, form_predictions
))
650 return scoped_ptr
<PasswordForm
>();
652 // No actual action on the form, so use the the origin as the action.
653 password_form
->action
= password_form
->origin
;
655 return password_form
.Pass();
658 } // namespace autofill