Home | History | Annotate | Download | only in renderer
      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.
      4 
      5 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
      6 
      7 #include "components/autofill/content/renderer/form_autofill_util.h"
      8 #include "components/autofill/core/common/password_form.h"
      9 #include "third_party/WebKit/public/platform/WebString.h"
     10 #include "third_party/WebKit/public/web/WebDocument.h"
     11 #include "third_party/WebKit/public/web/WebFormControlElement.h"
     12 #include "third_party/WebKit/public/web/WebInputElement.h"
     13 
     14 using blink::WebDocument;
     15 using blink::WebFormControlElement;
     16 using blink::WebFormElement;
     17 using blink::WebInputElement;
     18 using blink::WebString;
     19 using blink::WebVector;
     20 
     21 namespace autofill {
     22 namespace {
     23 
     24 // Maximum number of password fields we will observe before throwing our
     25 // hands in the air and giving up with a given form.
     26 static const size_t kMaxPasswords = 3;
     27 
     28 // Helper to determine which password is the main one, and which is
     29 // an old password (e.g on a "make new password" form), if any.
     30 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
     31                              WebInputElement* password,
     32                              WebInputElement* old_password) {
     33   switch (passwords.size()) {
     34     case 1:
     35       // Single password, easy.
     36       *password = passwords[0];
     37       break;
     38     case 2:
     39       if (passwords[0].value() == passwords[1].value()) {
     40         // Treat two identical passwords as a single password.
     41         *password = passwords[0];
     42       } else {
     43         // Assume first is old password, second is new (no choice but to guess).
     44         *old_password = passwords[0];
     45         *password = passwords[1];
     46       }
     47       break;
     48     case 3:
     49       if (passwords[0].value() == passwords[1].value() &&
     50           passwords[0].value() == passwords[2].value()) {
     51         // All three passwords the same? Just treat as one and hope.
     52         *password = passwords[0];
     53       } else if (passwords[0].value() == passwords[1].value()) {
     54         // Two the same and one different -> old password is duplicated one.
     55         *old_password = passwords[0];
     56         *password = passwords[2];
     57       } else if (passwords[1].value() == passwords[2].value()) {
     58         *old_password = passwords[0];
     59         *password = passwords[1];
     60       } else {
     61         // Three different passwords, or first and last match with middle
     62         // different. No idea which is which, so no luck.
     63         return false;
     64       }
     65       break;
     66     default:
     67       return false;
     68   }
     69   return true;
     70 }
     71 
     72 // Get information about a login form that encapsulated in the
     73 // PasswordForm struct.
     74 void GetPasswordForm(const WebFormElement& form, PasswordForm* password_form) {
     75   WebInputElement latest_input_element;
     76   std::vector<WebInputElement> passwords;
     77   std::vector<base::string16> other_possible_usernames;
     78 
     79   WebVector<WebFormControlElement> control_elements;
     80   form.getFormControlElements(control_elements);
     81 
     82   for (size_t i = 0; i < control_elements.size(); ++i) {
     83     WebFormControlElement control_element = control_elements[i];
     84     if (control_element.isActivatedSubmit())
     85       password_form->submit_element = control_element.formControlName();
     86 
     87     WebInputElement* input_element = toWebInputElement(&control_element);
     88     if (!input_element || !input_element->isEnabled())
     89       continue;
     90 
     91     if ((passwords.size() < kMaxPasswords) &&
     92         input_element->isPasswordField()) {
     93       // We assume that the username element is the input element before the
     94       // first password element.
     95       if (passwords.empty() && !latest_input_element.isNull()) {
     96         password_form->username_element =
     97             latest_input_element.nameForAutofill();
     98         password_form->username_value = latest_input_element.value();
     99         // Remove the selected username from other_possible_usernames.
    100         if (!other_possible_usernames.empty() &&
    101             !latest_input_element.value().isEmpty())
    102           other_possible_usernames.resize(other_possible_usernames.size() - 1);
    103       }
    104       passwords.push_back(*input_element);
    105     }
    106 
    107     // Various input types such as text, url, email can be a username field.
    108     if (input_element->isTextField() && !input_element->isPasswordField()) {
    109       latest_input_element = *input_element;
    110       // We ignore elements that have no value. Unlike username_element,
    111       // other_possible_usernames is used only for autofill, not for form
    112       // identification, and blank autofill entries are not useful.
    113       if (!input_element->value().isEmpty())
    114         other_possible_usernames.push_back(input_element->value());
    115     }
    116   }
    117 
    118   // Get the document URL
    119   GURL full_origin(form.document().url());
    120 
    121   // Calculate the canonical action URL
    122   WebString action = form.action();
    123   if (action.isNull())
    124     action = WebString(""); // missing 'action' attribute implies current URL
    125   GURL full_action(form.document().completeURL(action));
    126   if (!full_action.is_valid())
    127     return;
    128 
    129   WebInputElement password;
    130   WebInputElement old_password;
    131   if (!LocateSpecificPasswords(passwords, &password, &old_password))
    132     return;
    133 
    134   // We want to keep the path but strip any authentication data, as well as
    135   // query and ref portions of URL, for the form action and form origin.
    136   GURL::Replacements rep;
    137   rep.ClearUsername();
    138   rep.ClearPassword();
    139   rep.ClearQuery();
    140   rep.ClearRef();
    141   password_form->action = full_action.ReplaceComponents(rep);
    142   password_form->origin = full_origin.ReplaceComponents(rep);
    143 
    144   rep.SetPathStr("");
    145   password_form->signon_realm = full_origin.ReplaceComponents(rep).spec();
    146 
    147   password_form->other_possible_usernames.swap(other_possible_usernames);
    148 
    149   if (!password.isNull()) {
    150     password_form->password_element = password.nameForAutofill();
    151     password_form->password_value = password.value();
    152     password_form->password_autocomplete_set = password.autoComplete();
    153   }
    154   if (!old_password.isNull()) {
    155     password_form->old_password_element = old_password.nameForAutofill();
    156     password_form->old_password_value = old_password.value();
    157   }
    158 
    159   password_form->scheme = PasswordForm::SCHEME_HTML;
    160   password_form->ssl_valid = false;
    161   password_form->preferred = false;
    162   password_form->blacklisted_by_user = false;
    163   password_form->type = PasswordForm::TYPE_MANUAL;
    164   password_form->use_additional_authentication = false;
    165 }
    166 
    167 }  // namespace
    168 
    169 scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& web_form) {
    170   if (web_form.isNull())
    171     return scoped_ptr<PasswordForm>();
    172 
    173   scoped_ptr<PasswordForm> password_form(new PasswordForm());
    174   GetPasswordForm(web_form, password_form.get());
    175 
    176   if (!password_form->action.is_valid())
    177     return scoped_ptr<PasswordForm>();
    178 
    179   WebFormElementToFormData(web_form,
    180                            blink::WebFormControlElement(),
    181                            REQUIRE_NONE,
    182                            EXTRACT_NONE,
    183                            &password_form->form_data,
    184                            NULL /* FormFieldData */);
    185 
    186   return password_form.Pass();
    187 }
    188 
    189 }  // namespace autofill
    190