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