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 "base/utf_string_conversions.h" | |
6 #include "chrome/browser/autofill/autocomplete_history_manager.h" | |
7 #include "chrome/browser/autofill/autofill_external_delegate.h" | |
8 #include "chrome/browser/autofill/autofill_manager.h" | |
9 #include "components/autofill/common/autofill_messages.h" | |
10 #include "content/public/browser/navigation_controller.h" | |
11 #include "content/public/browser/notification_service.h" | |
12 #include "content/public/browser/notification_source.h" | |
13 #include "content/public/browser/notification_types.h" | |
14 #include "content/public/browser/render_view_host.h" | |
15 #include "content/public/browser/web_contents.h" | |
16 #include "grit/chromium_strings.h" | |
17 #include "grit/generated_resources.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | |
19 #include "ui/base/l10n/l10n_util.h" | |
20 | |
21 #if defined(OS_ANDROID) | |
22 #include "content/public/browser/android/content_view_core.h" | |
23 #endif | |
24 | |
25 using content::RenderViewHost; | |
26 using WebKit::WebAutofillClient; | |
27 | |
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(AutofillExternalDelegate); | |
29 | |
30 void AutofillExternalDelegate::CreateForWebContentsAndManager( | |
31 content::WebContents* web_contents, | |
32 AutofillManager* autofill_manager) { | |
33 if (FromWebContents(web_contents)) | |
34 return; | |
35 | |
36 web_contents->SetUserData( | |
37 UserDataKey(), | |
38 new AutofillExternalDelegate(web_contents, autofill_manager)); | |
39 } | |
40 | |
41 AutofillExternalDelegate::AutofillExternalDelegate( | |
42 content::WebContents* web_contents, | |
43 AutofillManager* autofill_manager) | |
44 : web_contents_(web_contents), | |
45 autofill_manager_(autofill_manager), | |
46 password_autofill_manager_(web_contents), | |
47 autofill_query_id_(0), | |
48 display_warning_if_disabled_(false), | |
49 has_autofill_suggestion_(false), | |
50 has_shown_autofill_popup_for_current_edit_(false), | |
51 registered_keyboard_listener_with_(NULL) { | |
52 DCHECK(autofill_manager); | |
53 | |
54 registrar_.Add(this, | |
55 content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED, | |
56 content::Source<content::WebContents>(web_contents)); | |
57 registrar_.Add( | |
58 this, | |
59 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
60 content::Source<content::NavigationController>( | |
61 &(web_contents->GetController()))); | |
62 } | |
63 | |
64 AutofillExternalDelegate::~AutofillExternalDelegate() {} | |
65 | |
66 void AutofillExternalDelegate::OnQuery(int query_id, | |
67 const FormData& form, | |
68 const FormFieldData& field, | |
69 const gfx::RectF& element_bounds, | |
70 bool display_warning_if_disabled) { | |
71 autofill_query_form_ = form; | |
72 autofill_query_field_ = field; | |
73 display_warning_if_disabled_ = display_warning_if_disabled; | |
74 autofill_query_id_ = query_id; | |
75 element_bounds_ = element_bounds; | |
76 } | |
77 | |
78 void AutofillExternalDelegate::OnSuggestionsReturned( | |
79 int query_id, | |
80 const std::vector<string16>& autofill_values, | |
81 const std::vector<string16>& autofill_labels, | |
82 const std::vector<string16>& autofill_icons, | |
83 const std::vector<int>& autofill_unique_ids) { | |
84 if (query_id != autofill_query_id_) | |
85 return; | |
86 | |
87 std::vector<string16> values(autofill_values); | |
88 std::vector<string16> labels(autofill_labels); | |
89 std::vector<string16> icons(autofill_icons); | |
90 std::vector<int> ids(autofill_unique_ids); | |
91 | |
92 // Add a separator to go between the values and menu items. | |
93 values.push_back(string16()); | |
94 labels.push_back(string16()); | |
95 icons.push_back(string16()); | |
96 ids.push_back(WebAutofillClient::MenuItemIDSeparator); | |
97 | |
98 ApplyAutofillWarnings(&values, &labels, &icons, &ids); | |
99 | |
100 // Only include "Autofill Options" special menu item if we have Autofill | |
101 // suggestions. | |
102 has_autofill_suggestion_ = false; | |
103 for (size_t i = 0; i < ids.size(); ++i) { | |
104 if (ids[i] > 0) { | |
105 has_autofill_suggestion_ = true; | |
106 break; | |
107 } | |
108 } | |
109 | |
110 if (has_autofill_suggestion_) | |
111 ApplyAutofillOptions(&values, &labels, &icons, &ids); | |
112 | |
113 // Remove the separator if it is the last element. | |
114 if (ids.back() == WebAutofillClient::MenuItemIDSeparator) { | |
115 values.pop_back(); | |
116 labels.pop_back(); | |
117 icons.pop_back(); | |
118 ids.pop_back(); | |
119 } | |
120 | |
121 InsertDataListValues(&values, &labels, &icons, &ids); | |
122 | |
123 if (values.empty()) { | |
124 // No suggestions, any popup currently showing is obsolete. | |
125 autofill_manager_->delegate()->HideAutofillPopup(); | |
126 return; | |
127 } | |
128 | |
129 // Send to display. | |
130 if (autofill_query_field_.is_focusable) { | |
131 autofill_manager_->delegate()->ShowAutofillPopup( | |
132 element_bounds_, values, labels, icons, ids, this); | |
133 } | |
134 } | |
135 | |
136 void AutofillExternalDelegate::OnShowPasswordSuggestions( | |
137 const std::vector<string16>& suggestions, | |
138 const FormFieldData& field, | |
139 const gfx::RectF& element_bounds) { | |
140 autofill_query_field_ = field; | |
141 element_bounds_ = element_bounds; | |
142 | |
143 if (suggestions.empty()) { | |
144 autofill_manager_->delegate()->HideAutofillPopup(); | |
145 return; | |
146 } | |
147 | |
148 std::vector<string16> empty(suggestions.size()); | |
149 std::vector<int> password_ids(suggestions.size(), | |
150 WebAutofillClient::MenuItemIDPasswordEntry); | |
151 autofill_manager_->delegate()->ShowAutofillPopup( | |
152 element_bounds_, suggestions, empty, empty, password_ids, this); | |
153 } | |
154 | |
155 void AutofillExternalDelegate::SetCurrentDataListValues( | |
156 const std::vector<string16>& data_list_values, | |
157 const std::vector<string16>& data_list_labels, | |
158 const std::vector<string16>& data_list_icons, | |
159 const std::vector<int>& data_list_unique_ids) { | |
160 data_list_values_ = data_list_values; | |
161 data_list_labels_ = data_list_labels; | |
162 data_list_icons_ = data_list_icons; | |
163 data_list_unique_ids_ = data_list_unique_ids; | |
164 } | |
165 | |
166 void AutofillExternalDelegate::OnPopupShown( | |
167 content::KeyboardListener* listener) { | |
168 if (!registered_keyboard_listener_with_) { | |
169 registered_keyboard_listener_with_ = web_contents_->GetRenderViewHost(); | |
170 registered_keyboard_listener_with_->AddKeyboardListener(listener); | |
171 } | |
172 | |
173 autofill_manager_->OnDidShowAutofillSuggestions( | |
174 has_autofill_suggestion_ && !has_shown_autofill_popup_for_current_edit_); | |
175 has_shown_autofill_popup_for_current_edit_ |= has_autofill_suggestion_; | |
176 } | |
177 | |
178 void AutofillExternalDelegate::OnPopupHidden( | |
179 content::KeyboardListener* listener) { | |
180 if (registered_keyboard_listener_with_ == web_contents_->GetRenderViewHost()) | |
181 web_contents_->GetRenderViewHost()->RemoveKeyboardListener(listener); | |
182 | |
183 registered_keyboard_listener_with_ = NULL; | |
184 } | |
185 | |
186 void AutofillExternalDelegate::DidSelectSuggestion(int identifier) { | |
187 ClearPreviewedForm(); | |
188 | |
189 // Only preview the data if it is a profile. | |
190 if (identifier > 0) | |
191 FillAutofillFormData(identifier, true); | |
192 } | |
193 | |
194 void AutofillExternalDelegate::DidAcceptSuggestion(const string16& value, | |
195 int identifier) { | |
196 RenderViewHost* host = web_contents_->GetRenderViewHost(); | |
197 | |
198 if (identifier == WebAutofillClient::MenuItemIDAutofillOptions) { | |
199 // User selected 'Autofill Options'. | |
200 autofill_manager_->OnShowAutofillDialog(); | |
201 } else if (identifier == WebAutofillClient::MenuItemIDClearForm) { | |
202 // User selected 'Clear form'. | |
203 host->Send(new AutofillMsg_ClearForm(host->GetRoutingID())); | |
204 } else if (identifier == WebAutofillClient::MenuItemIDPasswordEntry && | |
205 password_autofill_manager_.DidAcceptAutofillSuggestion( | |
206 autofill_query_field_, value)) { | |
207 // DidAcceptAutofillSuggestion has already handled the work to fill in | |
208 // the page as required. | |
209 } else if (identifier == WebAutofillClient::MenuItemIDDataListEntry) { | |
210 host->Send(new AutofillMsg_AcceptDataListSuggestion(host->GetRoutingID(), | |
211 value)); | |
212 } else if (identifier == WebAutofillClient::MenuItemIDAutocompleteEntry) { | |
213 // User selected an Autocomplete, so we fill directly. | |
214 host->Send(new AutofillMsg_SetNodeText(host->GetRoutingID(), value)); | |
215 } else { | |
216 FillAutofillFormData(identifier, false); | |
217 } | |
218 | |
219 autofill_manager_->delegate()->HideAutofillPopup(); | |
220 } | |
221 | |
222 void AutofillExternalDelegate::RemoveSuggestion(const string16& value, | |
223 int identifier) { | |
224 if (identifier > 0) { | |
225 autofill_manager_->RemoveAutofillProfileOrCreditCard(identifier); | |
226 } else { | |
227 autofill_manager_->RemoveAutocompleteEntry(autofill_query_field_.name, | |
228 value); | |
229 } | |
230 } | |
231 | |
232 void AutofillExternalDelegate::DidEndTextFieldEditing() { | |
233 autofill_manager_->delegate()->HideAutofillPopup(); | |
234 | |
235 has_shown_autofill_popup_for_current_edit_ = false; | |
236 } | |
237 | |
238 void AutofillExternalDelegate::ClearPreviewedForm() { | |
239 RenderViewHost* host = web_contents_->GetRenderViewHost(); | |
240 if (host) | |
241 host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID())); | |
242 } | |
243 | |
244 void AutofillExternalDelegate::Reset() { | |
245 autofill_manager_->delegate()->HideAutofillPopup(); | |
246 | |
247 password_autofill_manager_.Reset(); | |
248 } | |
249 | |
250 void AutofillExternalDelegate::AddPasswordFormMapping( | |
251 const FormFieldData& form, | |
252 const PasswordFormFillData& fill_data) { | |
253 password_autofill_manager_.AddPasswordFormMapping(form, fill_data); | |
254 } | |
255 | |
256 void AutofillExternalDelegate::FillAutofillFormData(int unique_id, | |
257 bool is_preview) { | |
258 // If the selected element is a warning we don't want to do anything. | |
259 if (unique_id == WebAutofillClient::MenuItemIDWarningMessage) | |
260 return; | |
261 | |
262 RenderViewHost* host = web_contents_->GetRenderViewHost(); | |
263 | |
264 if (is_preview) { | |
265 host->Send(new AutofillMsg_SetAutofillActionPreview( | |
266 host->GetRoutingID())); | |
267 } else { | |
268 host->Send(new AutofillMsg_SetAutofillActionFill( | |
269 host->GetRoutingID())); | |
270 } | |
271 | |
272 // Fill the values for the whole form. | |
273 autofill_manager_->OnFillAutofillFormData(autofill_query_id_, | |
274 autofill_query_form_, | |
275 autofill_query_field_, | |
276 unique_id); | |
277 } | |
278 | |
279 void AutofillExternalDelegate::ApplyAutofillWarnings( | |
280 std::vector<string16>* autofill_values, | |
281 std::vector<string16>* autofill_labels, | |
282 std::vector<string16>* autofill_icons, | |
283 std::vector<int>* autofill_unique_ids) { | |
284 if (!autofill_query_field_.should_autocomplete) { | |
285 // If autofill is disabled and we had suggestions, show a warning instead. | |
286 autofill_values->assign( | |
287 1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); | |
288 autofill_labels->assign(1, string16()); | |
289 autofill_icons->assign(1, string16()); | |
290 autofill_unique_ids->assign(1, WebAutofillClient::MenuItemIDWarningMessage); | |
291 } else if (autofill_unique_ids->size() > 1 && | |
292 (*autofill_unique_ids)[0] == | |
293 WebAutofillClient::MenuItemIDWarningMessage) { | |
294 // If we received a warning instead of suggestions from autofill but regular | |
295 // suggestions from autocomplete, don't show the autofill warning. | |
296 autofill_values->erase(autofill_values->begin()); | |
297 autofill_labels->erase(autofill_labels->begin()); | |
298 autofill_icons->erase(autofill_icons->begin()); | |
299 autofill_unique_ids->erase(autofill_unique_ids->begin()); | |
300 } | |
301 | |
302 // If we were about to show a warning and we shouldn't, don't. | |
303 if (!autofill_unique_ids->empty() && | |
304 (*autofill_unique_ids)[0] == | |
305 WebAutofillClient::MenuItemIDWarningMessage && | |
306 !display_warning_if_disabled_) { | |
307 autofill_values->clear(); | |
308 autofill_labels->clear(); | |
309 autofill_icons->clear(); | |
310 autofill_unique_ids->clear(); | |
311 } | |
312 } | |
313 | |
314 void AutofillExternalDelegate::ApplyAutofillOptions( | |
315 std::vector<string16>* autofill_values, | |
316 std::vector<string16>* autofill_labels, | |
317 std::vector<string16>* autofill_icons, | |
318 std::vector<int>* autofill_unique_ids) { | |
319 // The form has been auto-filled, so give the user the chance to clear the | |
320 // form. Append the 'Clear form' menu item. | |
321 if (autofill_query_field_.is_autofilled) { | |
322 autofill_values->push_back( | |
323 l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); | |
324 autofill_labels->push_back(string16()); | |
325 autofill_icons->push_back(string16()); | |
326 autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDClearForm); | |
327 } | |
328 | |
329 // Append the 'Chrome Autofill options' menu item; | |
330 autofill_values->push_back( | |
331 l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP)); | |
332 autofill_labels->push_back(string16()); | |
333 autofill_icons->push_back(string16()); | |
334 autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDAutofillOptions); | |
335 } | |
336 | |
337 void AutofillExternalDelegate::InsertDataListValues( | |
338 std::vector<string16>* autofill_values, | |
339 std::vector<string16>* autofill_labels, | |
340 std::vector<string16>* autofill_icons, | |
341 std::vector<int>* autofill_unique_ids) { | |
342 if (data_list_values_.empty()) | |
343 return; | |
344 | |
345 // Insert the separator between the datalist and Autofill values (if there | |
346 // are any). | |
347 if (!autofill_values->empty()) { | |
348 autofill_values->insert(autofill_values->begin(), string16()); | |
349 autofill_labels->insert(autofill_labels->begin(), string16()); | |
350 autofill_icons->insert(autofill_icons->begin(), string16()); | |
351 autofill_unique_ids->insert(autofill_unique_ids->begin(), | |
352 WebAutofillClient::MenuItemIDSeparator); | |
353 } | |
354 | |
355 // Insert the datalist elements. | |
356 autofill_values->insert(autofill_values->begin(), | |
357 data_list_values_.begin(), | |
358 data_list_values_.end()); | |
359 autofill_labels->insert(autofill_labels->begin(), | |
360 data_list_labels_.begin(), | |
361 data_list_labels_.end()); | |
362 autofill_icons->insert(autofill_icons->begin(), | |
363 data_list_icons_.begin(), | |
364 data_list_icons_.end()); | |
365 autofill_unique_ids->insert(autofill_unique_ids->begin(), | |
366 data_list_unique_ids_.begin(), | |
367 data_list_unique_ids_.end()); | |
368 } | |
369 | |
370 void AutofillExternalDelegate::Observe( | |
371 int type, | |
372 const content::NotificationSource& source, | |
373 const content::NotificationDetails& details) { | |
374 if (type == content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED) { | |
375 if (!*content::Details<bool>(details).ptr()) | |
376 autofill_manager_->delegate()->HideAutofillPopup(); | |
377 } else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { | |
378 autofill_manager_->delegate()->HideAutofillPopup(); | |
379 } else { | |
380 NOTREACHED(); | |
381 } | |
382 } | |
OLD | NEW |