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