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/command_line.h" 8 #include "base/logging.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "components/autofill/content/common/autofill_messages.h" 11 #include "components/autofill/content/renderer/form_autofill_util.h" 12 #include "components/autofill/content/renderer/password_form_conversion_utils.h" 13 #include "components/autofill/core/common/autofill_switches.h" 14 #include "components/autofill/core/common/form_data.h" 15 #include "components/autofill/core/common/password_form.h" 16 #include "components/autofill/core/common/password_generation_util.h" 17 #include "content/public/renderer/render_view.h" 18 #include "google_apis/gaia/gaia_urls.h" 19 #include "third_party/WebKit/public/platform/WebCString.h" 20 #include "third_party/WebKit/public/platform/WebRect.h" 21 #include "third_party/WebKit/public/platform/WebVector.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebFormElement.h" 24 #include "third_party/WebKit/public/web/WebInputElement.h" 25 #include "third_party/WebKit/public/web/WebLocalFrame.h" 26 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 27 #include "third_party/WebKit/public/web/WebView.h" 28 #include "ui/gfx/rect.h" 29 30 namespace autofill { 31 32 namespace { 33 34 // Returns true if we think that this form is for account creation. |passwords| 35 // is filled with the password field(s) in the form. 36 bool GetAccountCreationPasswordFields( 37 const blink::WebFormElement& form, 38 std::vector<blink::WebInputElement>* passwords) { 39 // Grab all of the passwords for the form. 40 blink::WebVector<blink::WebFormControlElement> control_elements; 41 form.getFormControlElements(control_elements); 42 43 size_t num_input_elements = 0; 44 for (size_t i = 0; i < control_elements.size(); i++) { 45 blink::WebInputElement* input_element = 46 toWebInputElement(&control_elements[i]); 47 // Only pay attention to visible password fields. 48 if (input_element && 49 input_element->isTextField() && 50 input_element->hasNonEmptyBoundingBox()) { 51 num_input_elements++; 52 if (input_element->isPasswordField()) 53 passwords->push_back(*input_element); 54 } 55 } 56 57 // This may be too lenient, but we assume that any form with at least three 58 // input elements where at least one of them is a password is an account 59 // creation form. 60 if (!passwords->empty() && num_input_elements >= 3) { 61 // We trim |passwords| because occasionally there are forms where the 62 // security question answers are put in password fields and we don't want 63 // to fill those. 64 if (passwords->size() > 2) 65 passwords->resize(2); 66 67 return true; 68 } 69 70 return false; 71 } 72 73 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) { 74 return std::find(urls.begin(), urls.end(), url) != urls.end(); 75 } 76 77 // Returns true if the |form1| is essentially equal to |form2|. 78 bool FormsAreEqual(const autofill::FormData& form1, 79 const PasswordForm& form2) { 80 // TODO(zysxqn): use more signals than just origin to compare. 81 // Note that FormData strips the fragement from the url while PasswordForm 82 // strips both the fragement and the path, so we can't just compare these 83 // two directly. 84 return form1.origin.GetOrigin() == form2.origin.GetOrigin(); 85 } 86 87 bool ContainsForm(const std::vector<autofill::FormData>& forms, 88 const PasswordForm& form) { 89 for (std::vector<autofill::FormData>::const_iterator it = 90 forms.begin(); it != forms.end(); ++it) { 91 if (FormsAreEqual(*it, form)) 92 return true; 93 } 94 return false; 95 } 96 97 } // namespace 98 99 PasswordGenerationAgent::PasswordGenerationAgent( 100 content::RenderView* render_view) 101 : content::RenderViewObserver(render_view), 102 render_view_(render_view), 103 password_is_generated_(false), 104 password_edited_(false), 105 enabled_(password_generation::IsPasswordGenerationEnabled()) { 106 DVLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled"); 107 } 108 PasswordGenerationAgent::~PasswordGenerationAgent() {} 109 110 void PasswordGenerationAgent::DidFinishDocumentLoad( 111 blink::WebLocalFrame* frame) { 112 // In every navigation, the IPC message sent by the password autofill manager 113 // to query whether the current form is blacklisted or not happens when the 114 // document load finishes, so we need to clear previous states here before we 115 // hear back from the browser. We only clear this state on main frame load 116 // as we don't want subframe loads to clear state that we have received from 117 // the main frame. Note that we assume there is only one account creation 118 // form, but there could be multiple password forms in each frame. 119 if (!frame->parent()) { 120 not_blacklisted_password_form_origins_.clear(); 121 generation_enabled_forms_.clear(); 122 generation_element_.reset(); 123 possible_account_creation_form_.reset(new PasswordForm()); 124 password_elements_.clear(); 125 password_is_generated_ = false; 126 if (password_edited_) { 127 password_generation::LogPasswordGenerationEvent( 128 password_generation::PASSWORD_EDITED); 129 } 130 password_edited_ = false; 131 } 132 } 133 134 void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) { 135 if (!enabled_) 136 return; 137 138 // We don't want to generate passwords if the browser won't store or sync 139 // them. 140 if (!ShouldAnalyzeDocument(frame->document())) 141 return; 142 143 blink::WebVector<blink::WebFormElement> forms; 144 frame->document().forms(forms); 145 for (size_t i = 0; i < forms.size(); ++i) { 146 if (forms[i].isNull()) 147 continue; 148 149 // If we can't get a valid PasswordForm, we skip this form because the 150 // the password won't get saved even if we generate it. 151 scoped_ptr<PasswordForm> password_form( 152 CreatePasswordForm(forms[i])); 153 if (!password_form.get()) { 154 DVLOG(2) << "Skipping form as it would not be saved"; 155 continue; 156 } 157 158 // Do not generate password for GAIA since it is used to retrieve the 159 // generated paswords. 160 GURL realm(password_form->signon_realm); 161 if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm()) 162 continue; 163 164 std::vector<blink::WebInputElement> passwords; 165 if (GetAccountCreationPasswordFields(forms[i], &passwords)) { 166 DVLOG(2) << "Account creation form detected"; 167 password_generation::LogPasswordGenerationEvent( 168 password_generation::SIGN_UP_DETECTED); 169 password_elements_ = passwords; 170 possible_account_creation_form_.swap(password_form); 171 DetermineGenerationElement(); 172 // We assume that there is only one account creation field per URL. 173 return; 174 } 175 } 176 password_generation::LogPasswordGenerationEvent( 177 password_generation::NO_SIGN_UP_DETECTED); 178 } 179 180 bool PasswordGenerationAgent::ShouldAnalyzeDocument( 181 const blink::WebDocument& document) const { 182 // Make sure that this security origin is allowed to use password manager. 183 // Generating a password that can't be saved is a bad idea. 184 blink::WebSecurityOrigin origin = document.securityOrigin(); 185 if (!origin.canAccessPasswordManager()) { 186 DVLOG(1) << "No PasswordManager access"; 187 return false; 188 } 189 190 return true; 191 } 192 193 bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) { 194 bool handled = true; 195 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationAgent, message) 196 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted, 197 OnFormNotBlacklisted) 198 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted, 199 OnPasswordAccepted) 200 IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected, 201 OnAccountCreationFormsDetected) 202 IPC_MESSAGE_UNHANDLED(handled = false) 203 IPC_END_MESSAGE_MAP() 204 return handled; 205 } 206 207 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) { 208 not_blacklisted_password_form_origins_.push_back(form.origin); 209 DetermineGenerationElement(); 210 } 211 212 void PasswordGenerationAgent::OnPasswordAccepted( 213 const base::string16& password) { 214 password_is_generated_ = true; 215 password_generation::LogPasswordGenerationEvent( 216 password_generation::PASSWORD_ACCEPTED); 217 for (std::vector<blink::WebInputElement>::iterator it = 218 password_elements_.begin(); 219 it != password_elements_.end(); ++it) { 220 it->setValue(password); 221 it->setAutofilled(true); 222 // Advance focus to the next input field. We assume password fields in 223 // an account creation form are always adjacent. 224 render_view_->GetWebView()->advanceFocus(false); 225 } 226 } 227 228 void PasswordGenerationAgent::OnAccountCreationFormsDetected( 229 const std::vector<autofill::FormData>& forms) { 230 generation_enabled_forms_.insert( 231 generation_enabled_forms_.end(), forms.begin(), forms.end()); 232 DetermineGenerationElement(); 233 } 234 235 void PasswordGenerationAgent::DetermineGenerationElement() { 236 // Make sure local heuristics have identified a possible account creation 237 // form. 238 if (!possible_account_creation_form_.get() || password_elements_.empty()) { 239 DVLOG(2) << "Local hueristics have not detected a possible account " 240 << "creation form"; 241 return; 242 } 243 244 if (CommandLine::ForCurrentProcess()->HasSwitch( 245 switches::kLocalHeuristicsOnlyForPasswordGeneration)) { 246 DVLOG(2) << "Bypassing additional checks."; 247 } else if (not_blacklisted_password_form_origins_.empty() || 248 !ContainsURL(not_blacklisted_password_form_origins_, 249 possible_account_creation_form_->origin)) { 250 DVLOG(2) << "Have not received confirmation that password form isn't " 251 << "blacklisted"; 252 return; 253 } else if (generation_enabled_forms_.empty() || 254 !ContainsForm(generation_enabled_forms_, 255 *possible_account_creation_form_)) { 256 // Note that this message will never be sent if this feature is disabled 257 // (e.g. Password saving is disabled). 258 DVLOG(2) << "Have not received confirmation from Autofill that form is " 259 << "used for account creation"; 260 return; 261 } 262 263 DVLOG(2) << "Password generation eligible form found"; 264 generation_element_ = password_elements_[0]; 265 password_generation::LogPasswordGenerationEvent( 266 password_generation::GENERATION_AVAILABLE); 267 } 268 269 bool PasswordGenerationAgent::FocusedNodeHasChanged( 270 const blink::WebNode& node) { 271 if (!generation_element_.isNull()) 272 generation_element_.setShouldRevealPassword(false); 273 274 if (node.isNull() || !node.isElementNode()) 275 return false; 276 277 const blink::WebElement web_element = node.toConst<blink::WebElement>(); 278 if (!web_element.document().frame()) 279 return false; 280 281 const blink::WebInputElement* element = toWebInputElement(&web_element); 282 if (!element || *element != generation_element_) 283 return false; 284 285 if (password_is_generated_) { 286 generation_element_.setShouldRevealPassword(true); 287 ShowEditingPopup(); 288 return true; 289 } 290 291 // Only trigger if the password field is empty. 292 if (!element->isReadOnly() && 293 element->isEnabled() && 294 element->value().isEmpty()) { 295 ShowGenerationPopup(); 296 return true; 297 } 298 299 return false; 300 } 301 302 bool PasswordGenerationAgent::TextDidChangeInTextField( 303 const blink::WebInputElement& element) { 304 if (element != generation_element_) 305 return false; 306 307 if (element.value().isEmpty()) { 308 if (password_is_generated_) { 309 // User generated a password and then deleted it. 310 password_generation::LogPasswordGenerationEvent( 311 password_generation::PASSWORD_DELETED); 312 } 313 314 // Do not treat the password as generated. 315 // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL. 316 password_is_generated_ = false; 317 generation_element_.setShouldRevealPassword(false); 318 319 // Offer generation again. 320 ShowGenerationPopup(); 321 } else if (!password_is_generated_) { 322 // User has rejected the feature and has started typing a password. 323 HidePopup(); 324 } else { 325 password_edited_ = true; 326 // Mirror edits to any confirmation password fields. 327 for (std::vector<blink::WebInputElement>::iterator it = 328 password_elements_.begin(); 329 it != password_elements_.end(); ++it) { 330 it->setValue(element.value()); 331 } 332 } 333 334 return true; 335 } 336 337 void PasswordGenerationAgent::ShowGenerationPopup() { 338 gfx::RectF bounding_box_scaled = 339 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(), 340 &generation_element_); 341 342 Send(new AutofillHostMsg_ShowPasswordGenerationPopup( 343 routing_id(), 344 bounding_box_scaled, 345 generation_element_.maxLength(), 346 *possible_account_creation_form_)); 347 348 password_generation::LogPasswordGenerationEvent( 349 password_generation::GENERATION_POPUP_SHOWN); 350 } 351 352 void PasswordGenerationAgent::ShowEditingPopup() { 353 gfx::RectF bounding_box_scaled = 354 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(), 355 &generation_element_); 356 357 Send(new AutofillHostMsg_ShowPasswordEditingPopup( 358 routing_id(), 359 bounding_box_scaled, 360 *possible_account_creation_form_)); 361 362 password_generation::LogPasswordGenerationEvent( 363 password_generation::EDITING_POPUP_SHOWN); 364 } 365 366 void PasswordGenerationAgent::HidePopup() { 367 Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id())); 368 } 369 370 } // namespace autofill 371