OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "chrome/renderer/autofill/password_generation_manager.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "chrome/common/password_generation_util.h" | |
9 #include "components/autofill/common/autofill_messages.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/Source/Platform/chromium/public/WebCString.h" | |
14 #include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" | |
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h" | |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/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()->addTextFieldDecoratorClient(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 bool PasswordGenerationManager::shouldAddDecorationTo( | |
154 const WebKit::WebInputElement& element) { | |
155 return element.isPasswordField(); | |
156 } | |
157 | |
158 bool PasswordGenerationManager::visibleByDefault() { | |
159 return false; | |
160 } | |
161 | |
162 WebKit::WebCString PasswordGenerationManager::imageNameForNormalState() { | |
163 return WebKit::WebCString("generatePassword"); | |
164 } | |
165 | |
166 WebKit::WebCString PasswordGenerationManager::imageNameForDisabledState() { | |
167 return imageNameForNormalState(); | |
168 } | |
169 | |
170 WebKit::WebCString PasswordGenerationManager::imageNameForReadOnlyState() { | |
171 return imageNameForNormalState(); | |
172 } | |
173 | |
174 WebKit::WebCString PasswordGenerationManager::imageNameForHoverState() { | |
175 return WebKit::WebCString("generatePasswordHover"); | |
176 } | |
177 | |
178 void PasswordGenerationManager::handleClick(WebKit::WebInputElement& element) { | |
179 gfx::Rect rect(element.decorationElementFor(this).boundsInViewportSpace()); | |
180 scoped_ptr<content::PasswordForm> password_form( | |
181 content::CreatePasswordForm(element.form())); | |
182 // We should not have shown the icon we can't create a valid PasswordForm. | |
183 DCHECK(password_form.get()); | |
184 | |
185 Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(), | |
186 rect, | |
187 element.maxLength(), | |
188 *password_form)); | |
189 password_generation::LogPasswordGenerationEvent( | |
190 password_generation::BUBBLE_SHOWN); | |
191 } | |
192 | |
193 void PasswordGenerationManager::willDetach( | |
194 const WebKit::WebInputElement& element) { | |
195 // No implementation | |
196 } | |
197 | |
198 bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) { | |
199 bool handled = true; | |
200 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationManager, message) | |
201 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted, | |
202 OnFormNotBlacklisted) | |
203 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted, | |
204 OnPasswordAccepted) | |
205 IPC_MESSAGE_HANDLER(AutofillMsg_PasswordGenerationEnabled, | |
206 OnPasswordGenerationEnabled) | |
207 IPC_MESSAGE_UNHANDLED(handled = false) | |
208 IPC_END_MESSAGE_MAP() | |
209 return handled; | |
210 } | |
211 | |
212 void PasswordGenerationManager::OnFormNotBlacklisted( | |
213 const content::PasswordForm& form) { | |
214 not_blacklisted_password_form_origins_.push_back(form.origin); | |
215 MaybeShowIcon(); | |
216 } | |
217 | |
218 void PasswordGenerationManager::OnPasswordAccepted(const string16& password) { | |
219 for (std::vector<WebKit::WebInputElement>::iterator it = passwords_.begin(); | |
220 it != passwords_.end(); ++it) { | |
221 it->setValue(password); | |
222 it->setAutofilled(true); | |
223 // Advance focus to the next input field. We assume password fields in | |
224 // an account creation form are always adjacent. | |
225 render_view_->GetWebView()->advanceFocus(false); | |
226 } | |
227 } | |
228 | |
229 void PasswordGenerationManager::OnPasswordGenerationEnabled(bool enabled) { | |
230 enabled_ = enabled; | |
231 } | |
232 | |
233 void PasswordGenerationManager::MaybeShowIcon() { | |
234 // We should show the password generation icon only when we have detected | |
235 // account creation form and we have confirmed from browser that this form | |
236 // is not blacklisted by the users. | |
237 if (!account_creation_form_origin_.is_valid() || | |
238 passwords_.empty() || | |
239 not_blacklisted_password_form_origins_.empty()) { | |
240 return; | |
241 } | |
242 | |
243 for (std::vector<GURL>::iterator it = | |
244 not_blacklisted_password_form_origins_.begin(); | |
245 it != not_blacklisted_password_form_origins_.end(); ++it) { | |
246 if (*it == account_creation_form_origin_) { | |
247 passwords_[0].decorationElementFor(this).setAttribute("style", | |
248 "display:block"); | |
249 password_generation::LogPasswordGenerationEvent( | |
250 password_generation::ICON_SHOWN); | |
251 return; | |
252 } | |
253 } | |
254 } | |
255 | |
256 } // namespace autofill | |
OLD | NEW |