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