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