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/core/browser/autofill_external_delegate.h"
6
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/autofill/core/browser/autocomplete_history_manager.h"
11 #include "components/autofill/core/browser/autofill_driver.h"
12 #include "components/autofill/core/browser/autofill_manager.h"
13 #include "components/autofill/core/browser/popup_item_ids.h"
14 #include "grit/components_strings.h"
15 #include "ui/base/l10n/l10n_util.h"
16
17 #if defined(OS_MACOSX) && !defined(OS_IOS)
18 namespace {
19
20 enum AccessAddressBookEventType {
21 // An Autofill entry was shown that prompts the user to give Chrome access to
22 // the user's Address Book.
23 SHOWED_ACCESS_ADDRESS_BOOK_ENTRY = 0,
24
25 // The user selected the Autofill entry which prompts Chrome to access the
26 // user's Address Book.
27 SELECTED_ACCESS_ADDRESS_BOOK_ENTRY = 1,
28
29 // Always keep this at the end.
30 ACCESS_ADDRESS_BOOK_ENTRY_MAX,
31 };
32
33 // Emits an entry for the histogram.
EmitHistogram(AccessAddressBookEventType type)34 void EmitHistogram(AccessAddressBookEventType type) {
35 UMA_HISTOGRAM_ENUMERATION(
36 "Autofill.MacAddressBook", type, ACCESS_ADDRESS_BOOK_ENTRY_MAX);
37 }
38
39 } // namespace
40 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
41
42 namespace autofill {
43
AutofillExternalDelegate(AutofillManager * manager,AutofillDriver * driver)44 AutofillExternalDelegate::AutofillExternalDelegate(AutofillManager* manager,
45 AutofillDriver* driver)
46 : manager_(manager),
47 driver_(driver),
48 query_id_(0),
49 display_warning_if_disabled_(false),
50 has_suggestion_(false),
51 has_shown_popup_for_current_edit_(false),
52 weak_ptr_factory_(this) {
53 DCHECK(manager);
54 }
55
~AutofillExternalDelegate()56 AutofillExternalDelegate::~AutofillExternalDelegate() {}
57
OnQuery(int query_id,const FormData & form,const FormFieldData & field,const gfx::RectF & element_bounds,bool display_warning_if_disabled)58 void AutofillExternalDelegate::OnQuery(int query_id,
59 const FormData& form,
60 const FormFieldData& field,
61 const gfx::RectF& element_bounds,
62 bool display_warning_if_disabled) {
63 query_form_ = form;
64 query_field_ = field;
65 display_warning_if_disabled_ = display_warning_if_disabled;
66 query_id_ = query_id;
67 element_bounds_ = element_bounds;
68 }
69
OnSuggestionsReturned(int query_id,const std::vector<base::string16> & suggested_values,const std::vector<base::string16> & suggested_labels,const std::vector<base::string16> & suggested_icons,const std::vector<int> & suggested_unique_ids)70 void AutofillExternalDelegate::OnSuggestionsReturned(
71 int query_id,
72 const std::vector<base::string16>& suggested_values,
73 const std::vector<base::string16>& suggested_labels,
74 const std::vector<base::string16>& suggested_icons,
75 const std::vector<int>& suggested_unique_ids) {
76 if (query_id != query_id_)
77 return;
78
79 std::vector<base::string16> values(suggested_values);
80 std::vector<base::string16> labels(suggested_labels);
81 std::vector<base::string16> icons(suggested_icons);
82 std::vector<int> ids(suggested_unique_ids);
83
84 // Add or hide warnings as appropriate.
85 ApplyAutofillWarnings(&values, &labels, &icons, &ids);
86
87 // Add a separator to go between the values and menu items.
88 values.push_back(base::string16());
89 labels.push_back(base::string16());
90 icons.push_back(base::string16());
91 ids.push_back(POPUP_ITEM_ID_SEPARATOR);
92
93 // Only include "Autofill Options" special menu item if we have Autofill
94 // suggestions.
95 has_suggestion_ = false;
96 for (size_t i = 0; i < ids.size(); ++i) {
97 if (ids[i] > 0) {
98 has_suggestion_ = true;
99 break;
100 }
101 }
102
103 if (has_suggestion_)
104 ApplyAutofillOptions(&values, &labels, &icons, &ids);
105
106 // Remove the separator if it is the last element.
107 DCHECK_GT(ids.size(), 0U);
108 if (ids.back() == POPUP_ITEM_ID_SEPARATOR) {
109 values.pop_back();
110 labels.pop_back();
111 icons.pop_back();
112 ids.pop_back();
113 }
114
115 // If anything else is added to modify the values after inserting the data
116 // list, AutofillPopupControllerImpl::UpdateDataListValues will need to be
117 // updated to match.
118 InsertDataListValues(&values, &labels, &icons, &ids);
119
120 #if defined(OS_MACOSX) && !defined(OS_IOS)
121 if (values.empty() &&
122 manager_->ShouldShowAccessAddressBookSuggestion(query_form_,
123 query_field_)) {
124 values.push_back(
125 l10n_util::GetStringUTF16(IDS_AUTOFILL_ACCESS_MAC_CONTACTS));
126 labels.push_back(base::string16());
127 icons.push_back(base::ASCIIToUTF16("macContactsIcon"));
128 ids.push_back(POPUP_ITEM_ID_MAC_ACCESS_CONTACTS);
129
130 EmitHistogram(SHOWED_ACCESS_ADDRESS_BOOK_ENTRY);
131 }
132 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
133
134 if (values.empty()) {
135 // No suggestions, any popup currently showing is obsolete.
136 manager_->client()->HideAutofillPopup();
137 return;
138 }
139
140 // Send to display.
141 if (query_field_.is_focusable) {
142 manager_->client()->ShowAutofillPopup(element_bounds_,
143 query_field_.text_direction,
144 values,
145 labels,
146 icons,
147 ids,
148 GetWeakPtr());
149 }
150 }
151
SetCurrentDataListValues(const std::vector<base::string16> & data_list_values,const std::vector<base::string16> & data_list_labels)152 void AutofillExternalDelegate::SetCurrentDataListValues(
153 const std::vector<base::string16>& data_list_values,
154 const std::vector<base::string16>& data_list_labels) {
155 data_list_values_ = data_list_values;
156 data_list_labels_ = data_list_labels;
157
158 manager_->client()->UpdateAutofillPopupDataListValues(data_list_values,
159 data_list_labels);
160 }
161
OnPopupShown()162 void AutofillExternalDelegate::OnPopupShown() {
163 manager_->DidShowSuggestions(
164 has_suggestion_ && !has_shown_popup_for_current_edit_);
165 has_shown_popup_for_current_edit_ |= has_suggestion_;
166 }
167
OnPopupHidden()168 void AutofillExternalDelegate::OnPopupHidden() {
169 }
170
DidSelectSuggestion(const base::string16 & value,int identifier)171 void AutofillExternalDelegate::DidSelectSuggestion(
172 const base::string16& value,
173 int identifier) {
174 ClearPreviewedForm();
175
176 // Only preview the data if it is a profile.
177 if (identifier > 0)
178 FillAutofillFormData(identifier, true);
179 else if (identifier == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY)
180 driver_->RendererShouldPreviewFieldWithValue(value);
181 }
182
DidAcceptSuggestion(const base::string16 & value,int identifier)183 void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value,
184 int identifier) {
185 if (identifier == POPUP_ITEM_ID_AUTOFILL_OPTIONS) {
186 // User selected 'Autofill Options'.
187 manager_->ShowAutofillSettings();
188 } else if (identifier == POPUP_ITEM_ID_CLEAR_FORM) {
189 // User selected 'Clear form'.
190 driver_->RendererShouldClearFilledForm();
191 } else if (identifier == POPUP_ITEM_ID_PASSWORD_ENTRY) {
192 NOTREACHED(); // Should be handled elsewhere.
193 } else if (identifier == POPUP_ITEM_ID_DATALIST_ENTRY) {
194 driver_->RendererShouldAcceptDataListSuggestion(value);
195 } else if (identifier == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
196 // User selected an Autocomplete, so we fill directly.
197 driver_->RendererShouldFillFieldWithValue(value);
198 } else if (identifier == POPUP_ITEM_ID_MAC_ACCESS_CONTACTS) {
199 #if defined(OS_MACOSX) && !defined(OS_IOS)
200 EmitHistogram(SELECTED_ACCESS_ADDRESS_BOOK_ENTRY);
201
202 // User wants to give Chrome access to user's address book.
203 manager_->AccessAddressBook();
204
205 // There is no deterministic method for deciding whether a blocking dialog
206 // was presented. The following comments and code assume that a blocking
207 // dialog was presented, but still behave correctly if no dialog was
208 // presented.
209
210 // A blocking dialog was presented, and the user has already responded to
211 // the dialog. The presentation of the dialog added an NSEvent to the
212 // NSRunLoop which will cause all windows to lose focus. When the NSEvent
213 // is processed, it will be sent to the renderer which will cause the text
214 // field to lose focus. This returns an IPC to Chrome which will dismiss
215 // the Autofill popup. We post a task which we expect to run after the
216 // NSEvent has been processed by the NSRunLoop. It pings the renderer,
217 // which returns an IPC acknowledging the ping. At that time, redisplay
218 // the popup. FIFO processing of IPCs ensures that all side effects of the
219 // NSEvent will have been processed.
220
221 // 10ms sits nicely under the 16ms threshold for 60 fps, and likely gives
222 // the NSApplication run loop sufficient time to process the NSEvent. In
223 // testing, a delay of 0ms was always sufficient.
224 base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
225 base::MessageLoop::current()->PostDelayedTask(
226 FROM_HERE,
227 base::Bind(&AutofillExternalDelegate::PingRenderer, GetWeakPtr()),
228 delay);
229 #else
230 NOTREACHED();
231 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
232 } else {
233 FillAutofillFormData(identifier, false);
234 }
235
236 manager_->client()->HideAutofillPopup();
237 }
238
RemoveSuggestion(const base::string16 & value,int identifier)239 void AutofillExternalDelegate::RemoveSuggestion(const base::string16& value,
240 int identifier) {
241 if (identifier > 0)
242 manager_->RemoveAutofillProfileOrCreditCard(identifier);
243 else
244 manager_->RemoveAutocompleteEntry(query_field_.name, value);
245 }
246
DidEndTextFieldEditing()247 void AutofillExternalDelegate::DidEndTextFieldEditing() {
248 manager_->client()->HideAutofillPopup();
249
250 has_shown_popup_for_current_edit_ = false;
251 }
252
ClearPreviewedForm()253 void AutofillExternalDelegate::ClearPreviewedForm() {
254 driver_->RendererShouldClearPreviewedForm();
255 }
256
Reset()257 void AutofillExternalDelegate::Reset() {
258 manager_->client()->HideAutofillPopup();
259 }
260
OnPingAck()261 void AutofillExternalDelegate::OnPingAck() {
262 // Reissue the most recent query, which will reopen the Autofill popup.
263 manager_->OnQueryFormFieldAutofill(query_id_,
264 query_form_,
265 query_field_,
266 element_bounds_,
267 display_warning_if_disabled_);
268 }
269
GetWeakPtr()270 base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() {
271 return weak_ptr_factory_.GetWeakPtr();
272 }
273
FillAutofillFormData(int unique_id,bool is_preview)274 void AutofillExternalDelegate::FillAutofillFormData(int unique_id,
275 bool is_preview) {
276 // If the selected element is a warning we don't want to do anything.
277 if (unique_id == POPUP_ITEM_ID_WARNING_MESSAGE)
278 return;
279
280 AutofillDriver::RendererFormDataAction renderer_action = is_preview ?
281 AutofillDriver::FORM_DATA_ACTION_PREVIEW :
282 AutofillDriver::FORM_DATA_ACTION_FILL;
283
284 DCHECK(driver_->RendererIsAvailable());
285 // Fill the values for the whole form.
286 manager_->FillOrPreviewForm(renderer_action,
287 query_id_,
288 query_form_,
289 query_field_,
290 unique_id);
291 }
292
ApplyAutofillWarnings(std::vector<base::string16> * values,std::vector<base::string16> * labels,std::vector<base::string16> * icons,std::vector<int> * unique_ids)293 void AutofillExternalDelegate::ApplyAutofillWarnings(
294 std::vector<base::string16>* values,
295 std::vector<base::string16>* labels,
296 std::vector<base::string16>* icons,
297 std::vector<int>* unique_ids) {
298 if (!query_field_.should_autocomplete) {
299 // Autofill is disabled. If there were some profile or credit card
300 // suggestions to show, show a warning instead. Otherwise, clear out the
301 // list of suggestions.
302 if (!unique_ids->empty() && (*unique_ids)[0] > 0) {
303 // If Autofill is disabled and we had suggestions, show a warning instead.
304 values->assign(
305 1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
306 labels->assign(1, base::string16());
307 icons->assign(1, base::string16());
308 unique_ids->assign(1, POPUP_ITEM_ID_WARNING_MESSAGE);
309 } else {
310 values->clear();
311 labels->clear();
312 icons->clear();
313 unique_ids->clear();
314 }
315 } else if (unique_ids->size() > 1 &&
316 (*unique_ids)[0] == POPUP_ITEM_ID_WARNING_MESSAGE) {
317 // If we received a warning instead of suggestions from Autofill but regular
318 // suggestions from autocomplete, don't show the Autofill warning.
319 values->erase(values->begin());
320 labels->erase(labels->begin());
321 icons->erase(icons->begin());
322 unique_ids->erase(unique_ids->begin());
323 }
324
325 // If we were about to show a warning and we shouldn't, don't.
326 if (!unique_ids->empty() &&
327 (*unique_ids)[0] == POPUP_ITEM_ID_WARNING_MESSAGE &&
328 !display_warning_if_disabled_) {
329 values->clear();
330 labels->clear();
331 icons->clear();
332 unique_ids->clear();
333 }
334 }
335
ApplyAutofillOptions(std::vector<base::string16> * values,std::vector<base::string16> * labels,std::vector<base::string16> * icons,std::vector<int> * unique_ids)336 void AutofillExternalDelegate::ApplyAutofillOptions(
337 std::vector<base::string16>* values,
338 std::vector<base::string16>* labels,
339 std::vector<base::string16>* icons,
340 std::vector<int>* unique_ids) {
341 // The form has been auto-filled, so give the user the chance to clear the
342 // form. Append the 'Clear form' menu item.
343 if (query_field_.is_autofilled) {
344 values->push_back(
345 l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
346 labels->push_back(base::string16());
347 icons->push_back(base::string16());
348 unique_ids->push_back(POPUP_ITEM_ID_CLEAR_FORM);
349 }
350
351 // Append the 'Chrome Autofill options' menu item;
352 values->push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
353 labels->push_back(base::string16());
354 icons->push_back(base::string16());
355 unique_ids->push_back(POPUP_ITEM_ID_AUTOFILL_OPTIONS);
356 }
357
InsertDataListValues(std::vector<base::string16> * values,std::vector<base::string16> * labels,std::vector<base::string16> * icons,std::vector<int> * unique_ids)358 void AutofillExternalDelegate::InsertDataListValues(
359 std::vector<base::string16>* values,
360 std::vector<base::string16>* labels,
361 std::vector<base::string16>* icons,
362 std::vector<int>* unique_ids) {
363 if (data_list_values_.empty())
364 return;
365
366 // Insert the separator between the datalist and Autofill values (if there
367 // are any).
368 if (!values->empty()) {
369 values->insert(values->begin(), base::string16());
370 labels->insert(labels->begin(), base::string16());
371 icons->insert(icons->begin(), base::string16());
372 unique_ids->insert(unique_ids->begin(), POPUP_ITEM_ID_SEPARATOR);
373 }
374
375 // Insert the datalist elements.
376 values->insert(values->begin(),
377 data_list_values_.begin(),
378 data_list_values_.end());
379 labels->insert(labels->begin(),
380 data_list_labels_.begin(),
381 data_list_labels_.end());
382
383 // Set the values that all datalist elements share.
384 icons->insert(icons->begin(),
385 data_list_values_.size(),
386 base::string16());
387 unique_ids->insert(unique_ids->begin(),
388 data_list_values_.size(),
389 POPUP_ITEM_ID_DATALIST_ENTRY);
390 }
391
392 #if defined(OS_MACOSX) && !defined(OS_IOS)
PingRenderer()393 void AutofillExternalDelegate::PingRenderer() {
394 driver_->PingRenderer();
395 }
396 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
397
398 } // namespace autofill
399