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)
18namespace {
19
20enum 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.
34void 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
42namespace autofill {
43
44AutofillExternalDelegate::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
56AutofillExternalDelegate::~AutofillExternalDelegate() {}
57
58void 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
70void 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
152void 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
162void 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
168void AutofillExternalDelegate::OnPopupHidden() {
169}
170
171void 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
183void 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
239void 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
247void AutofillExternalDelegate::DidEndTextFieldEditing() {
248  manager_->client()->HideAutofillPopup();
249
250  has_shown_popup_for_current_edit_ = false;
251}
252
253void AutofillExternalDelegate::ClearPreviewedForm() {
254  driver_->RendererShouldClearPreviewedForm();
255}
256
257void AutofillExternalDelegate::Reset() {
258  manager_->client()->HideAutofillPopup();
259}
260
261void 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
270base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() {
271  return weak_ptr_factory_.GetWeakPtr();
272}
273
274void 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
293void 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
336void 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
358void 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)
393void AutofillExternalDelegate::PingRenderer() {
394  driver_->PingRenderer();
395}
396#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
397
398}  // namespace autofill
399