• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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