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