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_generation_manager.h"
      6 
      7 #include "base/logging.h"
      8 #include "components/autofill/core/common/autofill_messages.h"
      9 #include "components/autofill/core/common/password_generation_util.h"
     10 #include "content/public/renderer/password_form_conversion_utils.h"
     11 #include "content/public/renderer/render_view.h"
     12 #include "google_apis/gaia/gaia_urls.h"
     13 #include "third_party/WebKit/public/platform/WebCString.h"
     14 #include "third_party/WebKit/public/platform/WebRect.h"
     15 #include "third_party/WebKit/public/platform/WebVector.h"
     16 #include "third_party/WebKit/public/web/WebDocument.h"
     17 #include "third_party/WebKit/public/web/WebFormElement.h"
     18 #include "third_party/WebKit/public/web/WebFrame.h"
     19 #include "third_party/WebKit/public/web/WebInputElement.h"
     20 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
     21 #include "third_party/WebKit/public/web/WebView.h"
     22 #include "ui/gfx/rect.h"
     23 
     24 namespace autofill {
     25 
     26 namespace {
     27 
     28 // Returns true if we think that this form is for account creation. |passwords|
     29 // is filled with the password field(s) in the form.
     30 bool GetAccountCreationPasswordFields(
     31     const WebKit::WebFormElement& form,
     32     std::vector<WebKit::WebInputElement>* passwords) {
     33   // Grab all of the passwords for the form.
     34   WebKit::WebVector<WebKit::WebFormControlElement> control_elements;
     35   form.getFormControlElements(control_elements);
     36 
     37   size_t num_input_elements = 0;
     38   for (size_t i = 0; i < control_elements.size(); i++) {
     39     WebKit::WebInputElement* input_element =
     40         toWebInputElement(&control_elements[i]);
     41     // Only pay attention to visible password fields.
     42     if (input_element &&
     43         input_element->isTextField() &&
     44         input_element->hasNonEmptyBoundingBox()) {
     45       num_input_elements++;
     46       if (input_element->isPasswordField())
     47         passwords->push_back(*input_element);
     48     }
     49   }
     50 
     51   // This may be too lenient, but we assume that any form with at least three
     52   // input elements where at least one of them is a password is an account
     53   // creation form.
     54   if (!passwords->empty() && num_input_elements >= 3) {
     55     // We trim |passwords| because occasionally there are forms where the
     56     // security question answers are put in password fields and we don't want
     57     // to fill those.
     58     if (passwords->size() > 2)
     59       passwords->resize(2);
     60 
     61     return true;
     62   }
     63 
     64   return false;
     65 }
     66 
     67 }  // namespace
     68 
     69 PasswordGenerationManager::PasswordGenerationManager(
     70     content::RenderView* render_view)
     71     : content::RenderViewObserver(render_view),
     72       render_view_(render_view),
     73       enabled_(false) {
     74   render_view_->GetWebView()->setPasswordGeneratorClient(this);
     75 }
     76 PasswordGenerationManager::~PasswordGenerationManager() {}
     77 
     78 void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
     79   // In every navigation, the IPC message sent by the password autofill manager
     80   // to query whether the current form is blacklisted or not happens when the
     81   // document load finishes, so we need to clear previous states here before we
     82   // hear back from the browser. We only clear this state on main frame load
     83   // as we don't want subframe loads to clear state that we have recieved from
     84   // the main frame. Note that we assume there is only one account creation
     85   // form, but there could be multiple password forms in each frame.
     86   if (!frame->parent()) {
     87     not_blacklisted_password_form_origins_.clear();
     88     // Initialize to an empty and invalid GURL.
     89     account_creation_form_origin_ = GURL();
     90     passwords_.clear();
     91   }
     92 }
     93 
     94 void PasswordGenerationManager::DidFinishLoad(WebKit::WebFrame* frame) {
     95   // We don't want to generate passwords if the browser won't store or sync
     96   // them.
     97   if (!enabled_)
     98     return;
     99 
    100   if (!ShouldAnalyzeDocument(frame->document()))
    101     return;
    102 
    103   WebKit::WebVector<WebKit::WebFormElement> forms;
    104   frame->document().forms(forms);
    105   for (size_t i = 0; i < forms.size(); ++i) {
    106     if (forms[i].isNull())
    107       continue;
    108 
    109     // If we can't get a valid PasswordForm, we skip this form because the
    110     // the password won't get saved even if we generate it.
    111     scoped_ptr<content::PasswordForm> password_form(
    112         content::CreatePasswordForm(forms[i]));
    113     if (!password_form.get()) {
    114       DVLOG(2) << "Skipping form as it would not be saved";
    115       continue;
    116     }
    117 
    118     // Do not generate password for GAIA since it is used to retrieve the
    119     // generated paswords.
    120     GURL realm(password_form->signon_realm);
    121     if (realm == GURL(GaiaUrls::GetInstance()->gaia_login_form_realm()))
    122       continue;
    123 
    124     std::vector<WebKit::WebInputElement> passwords;
    125     if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
    126       DVLOG(2) << "Account creation form detected";
    127       password_generation::LogPasswordGenerationEvent(
    128           password_generation::SIGN_UP_DETECTED);
    129       passwords_ = passwords;
    130       account_creation_form_origin_ = password_form->origin;
    131       MaybeShowIcon();
    132       // We assume that there is only one account creation field per URL.
    133       return;
    134     }
    135   }
    136   password_generation::LogPasswordGenerationEvent(
    137       password_generation::NO_SIGN_UP_DETECTED);
    138 }
    139 
    140 bool PasswordGenerationManager::ShouldAnalyzeDocument(
    141     const WebKit::WebDocument& document) const {
    142   // Make sure that this security origin is allowed to use password manager.
    143   // Generating a password that can't be saved is a bad idea.
    144   WebKit::WebSecurityOrigin origin = document.securityOrigin();
    145   if (!origin.canAccessPasswordManager()) {
    146     DVLOG(1) << "No PasswordManager access";
    147     return false;
    148   }
    149 
    150   return true;
    151 }
    152 
    153 void PasswordGenerationManager::openPasswordGenerator(
    154     WebKit::WebInputElement& element) {
    155   WebKit::WebElement button(element.passwordGeneratorButtonElement());
    156   gfx::Rect rect(button.boundsInViewportSpace());
    157   scoped_ptr<content::PasswordForm> password_form(
    158       content::CreatePasswordForm(element.form()));
    159   // We should not have shown the icon we can't create a valid PasswordForm.
    160   DCHECK(password_form.get());
    161 
    162   Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(),
    163                                                        rect,
    164                                                        element.maxLength(),
    165                                                        *password_form));
    166   password_generation::LogPasswordGenerationEvent(
    167       password_generation::BUBBLE_SHOWN);
    168 }
    169 
    170 bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
    171   bool handled = true;
    172   IPC_BEGIN_MESSAGE_MAP(PasswordGenerationManager, message)
    173     IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
    174                         OnFormNotBlacklisted)
    175     IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
    176                         OnPasswordAccepted)
    177     IPC_MESSAGE_HANDLER(AutofillMsg_PasswordGenerationEnabled,
    178                         OnPasswordGenerationEnabled)
    179     IPC_MESSAGE_UNHANDLED(handled = false)
    180   IPC_END_MESSAGE_MAP()
    181   return handled;
    182 }
    183 
    184 void PasswordGenerationManager::OnFormNotBlacklisted(
    185     const content::PasswordForm& form) {
    186   not_blacklisted_password_form_origins_.push_back(form.origin);
    187   MaybeShowIcon();
    188 }
    189 
    190 void PasswordGenerationManager::OnPasswordAccepted(
    191     const base::string16& password) {
    192   for (std::vector<WebKit::WebInputElement>::iterator it = passwords_.begin();
    193        it != passwords_.end(); ++it) {
    194     it->setValue(password);
    195     it->setAutofilled(true);
    196     // Advance focus to the next input field. We assume password fields in
    197     // an account creation form are always adjacent.
    198     render_view_->GetWebView()->advanceFocus(false);
    199   }
    200 }
    201 
    202 void PasswordGenerationManager::OnPasswordGenerationEnabled(bool enabled) {
    203   enabled_ = enabled;
    204 }
    205 
    206 void PasswordGenerationManager::MaybeShowIcon() {
    207   // We should show the password generation icon only when we have detected
    208   // account creation form and we have confirmed from browser that this form
    209   // is not blacklisted by the users.
    210   if (!account_creation_form_origin_.is_valid() ||
    211       passwords_.empty() ||
    212       not_blacklisted_password_form_origins_.empty()) {
    213     return;
    214   }
    215 
    216   for (std::vector<GURL>::iterator it =
    217            not_blacklisted_password_form_origins_.begin();
    218        it != not_blacklisted_password_form_origins_.end(); ++it) {
    219     if (*it == account_creation_form_origin_) {
    220       passwords_[0].passwordGeneratorButtonElement().setAttribute("style",
    221                                                             "display:block");
    222       password_generation::LogPasswordGenerationEvent(
    223           password_generation::ICON_SHOWN);
    224       return;
    225     }
    226   }
    227 }
    228 
    229 }  // namespace autofill
    230