Home | History | Annotate | Download | only in browser
      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.
     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 
     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 
     56 AutofillExternalDelegate::~AutofillExternalDelegate() {}
     57 
     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 
     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 
    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 
    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 
    168 void AutofillExternalDelegate::OnPopupHidden() {
    169 }
    170 
    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 
    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 
    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 
    247 void AutofillExternalDelegate::DidEndTextFieldEditing() {
    248   manager_->client()->HideAutofillPopup();
    249 
    250   has_shown_popup_for_current_edit_ = false;
    251 }
    252 
    253 void AutofillExternalDelegate::ClearPreviewedForm() {
    254   driver_->RendererShouldClearPreviewedForm();
    255 }
    256 
    257 void AutofillExternalDelegate::Reset() {
    258   manager_->client()->HideAutofillPopup();
    259 }
    260 
    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 
    270 base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() {
    271   return weak_ptr_factory_.GetWeakPtr();
    272 }
    273 
    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 
    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 
    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 
    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)
    393 void AutofillExternalDelegate::PingRenderer() {
    394   driver_->PingRenderer();
    395 }
    396 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
    397 
    398 }  // namespace autofill
    399