OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/autofill/autofill_dialog_controller.h" | 5 #include "chrome/browser/ui/autofill/autofill_dialog_controller.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/string_split.h" | 8 #include "base/string_split.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 const DetailInput* input = &input_template[i]; | 78 const DetailInput* input = &input_template[i]; |
79 for (size_t j = 0; j < form_structure.field_count(); ++j) { | 79 for (size_t j = 0; j < form_structure.field_count(); ++j) { |
80 if (DetailInputMatchesField(*input, *form_structure.field(j))) { | 80 if (DetailInputMatchesField(*input, *form_structure.field(j))) { |
81 inputs->push_back(*input); | 81 inputs->push_back(*input); |
82 break; | 82 break; |
83 } | 83 } |
84 } | 84 } |
85 } | 85 } |
86 } | 86 } |
87 | 87 |
88 // Finds the instance of T which has the most data for filling in the inputs in | 88 // Uses |group| to fill in the |autofilled_value| for all inputs in |all_inputs| |
89 // |all_inputs|. T should be a FormGroup type. | 89 // (an out-param). |
90 template<class T> FormGroup* GetBestDataSource( | 90 void FillInputFromFormGroup(FormGroup* group, DetailInputs* inputs) { |
91 const std::vector<T*>& sources, | |
92 const DetailInputs* const* all_inputs, | |
93 size_t all_inputs_length) { | |
94 const std::string app_locale = AutofillCountry::ApplicationLocale(); | 91 const std::string app_locale = AutofillCountry::ApplicationLocale(); |
95 | 92 for (size_t j = 0; j < inputs->size(); ++j) { |
96 int best_field_fill_count = 0; | 93 (*inputs)[j].autofilled_value = |
97 FormGroup* best_source = NULL; | 94 group->GetInfo((*inputs)[j].type, app_locale); |
98 | |
99 for (size_t i = 0; i < sources.size(); ++i) { | |
100 int field_fill_count = 0; | |
101 | |
102 for (size_t j = 0; j < all_inputs_length; ++j) { | |
103 const DetailInputs& inputs = *all_inputs[j]; | |
104 for (size_t k = 0; k < inputs.size(); ++k) { | |
105 if (!sources[i]->GetInfo(inputs[k].type, app_locale).empty()) | |
106 field_fill_count++; | |
107 } | |
108 } | |
109 | |
110 // TODO(estade): should there be a better tiebreaker? | |
111 if (field_fill_count > best_field_fill_count) { | |
112 best_field_fill_count = field_fill_count; | |
113 best_source = sources[i]; | |
114 } | |
115 } | |
116 | |
117 return best_source; | |
118 } | |
119 | |
120 // Uses |group| to fill in the |starting_value| for all inputs in |all_inputs| | |
121 // (an out-param). | |
122 void FillInputsFromFormGroup(FormGroup* group, | |
123 DetailInputs** all_inputs, | |
124 size_t all_inputs_length) { | |
125 const std::string app_locale = AutofillCountry::ApplicationLocale(); | |
126 for (size_t i = 0; i < all_inputs_length; ++i) { | |
127 DetailInputs& inputs = *all_inputs[i]; | |
128 for (size_t j = 0; j < inputs.size(); ++j) { | |
129 inputs[j].starting_value = group->GetInfo(inputs[j].type, app_locale); | |
130 } | |
131 } | 95 } |
132 } | 96 } |
133 | 97 |
134 // Initializes |form_group| from user-entered data. | 98 // Initializes |form_group| from user-entered data. |
135 void FillFormGroupFromOutputs(const DetailOutputMap& detail_outputs, | 99 void FillFormGroupFromOutputs(const DetailOutputMap& detail_outputs, |
136 FormGroup* form_group) { | 100 FormGroup* form_group) { |
137 for (DetailOutputMap::const_iterator iter = detail_outputs.begin(); | 101 for (DetailOutputMap::const_iterator iter = detail_outputs.begin(); |
138 iter != detail_outputs.end(); ++iter) { | 102 iter != detail_outputs.end(); ++iter) { |
139 if (!iter->second.empty()) | 103 if (!iter->second.empty()) |
140 form_group->SetRawInfo(iter->first->type, iter->second); | 104 form_group->SetRawInfo(iter->first->type, iter->second); |
141 } | 105 } |
142 } | 106 } |
143 | 107 |
144 } // namespace | 108 } // namespace |
145 | 109 |
146 AutofillDialogController::AutofillDialogController( | 110 AutofillDialogController::AutofillDialogController( |
147 content::WebContents* contents, | 111 content::WebContents* contents, |
148 const FormData& form, | 112 const FormData& form, |
149 const GURL& source_url, | 113 const GURL& source_url, |
150 const content::SSLStatus& ssl_status, | 114 const content::SSLStatus& ssl_status, |
151 const base::Callback<void(const FormStructure*)>& callback) | 115 const base::Callback<void(const FormStructure*)>& callback) |
152 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), | 116 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), |
153 contents_(contents), | 117 contents_(contents), |
154 form_structure_(form), | 118 form_structure_(form), |
155 source_url_(source_url), | 119 source_url_(source_url), |
156 ssl_status_(ssl_status), | 120 ssl_status_(ssl_status), |
157 callback_(callback) { | 121 callback_(callback), |
| 122 popup_controller_(NULL) { |
158 // TODO(estade): |this| should observe PersonalDataManager. | 123 // TODO(estade): |this| should observe PersonalDataManager. |
159 // TODO(estade): remove duplicates from |form|? | 124 // TODO(estade): remove duplicates from |form|? |
160 } | 125 } |
161 | 126 |
162 AutofillDialogController::~AutofillDialogController() {} | 127 AutofillDialogController::~AutofillDialogController() { |
| 128 if (popup_controller_) |
| 129 popup_controller_->Hide(); |
| 130 } |
163 | 131 |
164 void AutofillDialogController::Show() { | 132 void AutofillDialogController::Show() { |
165 bool has_types = false; | 133 bool has_types = false; |
166 bool has_sections = false; | 134 bool has_sections = false; |
167 form_structure_.ParseFieldTypesFromAutocompleteAttributes(&has_types, | 135 form_structure_.ParseFieldTypesFromAutocompleteAttributes(&has_types, |
168 &has_sections); | 136 &has_sections); |
169 // Fail if the author didn't specify autocomplete types. | 137 // Fail if the author didn't specify autocomplete types. |
170 if (!has_types) { | 138 if (!has_types) { |
171 callback_.Run(NULL); | 139 callback_.Run(NULL); |
172 delete this; | 140 delete this; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 arraysize(kBillingInputs), | 187 arraysize(kBillingInputs), |
220 &requested_billing_fields_); | 188 &requested_billing_fields_); |
221 | 189 |
222 FilterInputs(form_structure_, | 190 FilterInputs(form_structure_, |
223 kShippingInputs, | 191 kShippingInputs, |
224 arraysize(kShippingInputs), | 192 arraysize(kShippingInputs), |
225 &requested_shipping_fields_); | 193 &requested_shipping_fields_); |
226 | 194 |
227 // TODO(estade): make this actually check if it's a first run. | 195 // TODO(estade): make this actually check if it's a first run. |
228 bool first_run = true; | 196 bool first_run = true; |
229 if (first_run) | 197 if (!first_run) |
230 PopulateInputsWithGuesses(); | |
231 else | |
232 GenerateComboboxModels(); | 198 GenerateComboboxModels(); |
233 | 199 |
234 // TODO(estade): don't show the dialog if the site didn't specify the right | 200 // TODO(estade): don't show the dialog if the site didn't specify the right |
235 // fields. First we must figure out what the "right" fields are. | 201 // fields. First we must figure out what the "right" fields are. |
236 view_.reset(AutofillDialogView::Create(this)); | 202 view_.reset(AutofillDialogView::Create(this)); |
237 view_->Show(); | 203 view_->Show(); |
238 } | 204 } |
239 | 205 |
240 string16 AutofillDialogController::DialogTitle() const { | 206 string16 AutofillDialogController::DialogTitle() const { |
241 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE); | 207 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE); |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 FillOutputForSection(SECTION_SHIPPING); | 344 FillOutputForSection(SECTION_SHIPPING); |
379 } | 345 } |
380 callback_.Run(&form_structure_); | 346 callback_.Run(&form_structure_); |
381 } else { | 347 } else { |
382 callback_.Run(NULL); | 348 callback_.Run(NULL); |
383 } | 349 } |
384 | 350 |
385 delete this; | 351 delete this; |
386 } | 352 } |
387 | 353 |
| 354 void AutofillDialogController::UserEditedInput( |
| 355 const DetailInput* input, |
| 356 gfx::NativeView view, |
| 357 const gfx::Rect& content_bounds, |
| 358 const string16& field_contents) { |
| 359 // TODO(estade): support all sections, not just billing. |
| 360 std::vector<string16> popup_values, popup_labels, popup_icons; |
| 361 std::vector<AutofillFieldType> field_types; |
| 362 const DetailInputs& inputs = RequestedFieldsForSection(SECTION_BILLING); |
| 363 field_types.reserve(inputs.size()); |
| 364 for (DetailInputs::const_iterator iter = inputs.begin(); |
| 365 iter != inputs.end(); ++iter) { |
| 366 field_types.push_back(iter->type); |
| 367 } |
| 368 GetManager()->GetProfileSuggestions(input->type, |
| 369 field_contents, |
| 370 false, |
| 371 field_types, |
| 372 &popup_values, |
| 373 &popup_labels, |
| 374 &popup_icons, |
| 375 &popup_guids_); |
| 376 |
| 377 // TODO(estade): do we need separators and control rows like 'Clear |
| 378 // Form'? |
| 379 std::vector<int> popup_ids; |
| 380 for (size_t i = 0; i < popup_guids_.size(); ++i) { |
| 381 popup_ids.push_back(i); |
| 382 } |
| 383 |
| 384 popup_controller_ = AutofillPopupControllerImpl::GetOrCreate( |
| 385 popup_controller_, this, view, content_bounds); |
| 386 popup_controller_->Show(popup_values, |
| 387 popup_labels, |
| 388 popup_icons, |
| 389 popup_ids); |
| 390 } |
| 391 |
| 392 void AutofillDialogController::FocusMoved() { |
| 393 if (popup_controller_) { |
| 394 popup_controller_->Hide(); |
| 395 ControllerDestroyed(); |
| 396 } |
| 397 } |
| 398 |
| 399 void AutofillDialogController::DidSelectSuggestion(int identifier) { |
| 400 // TODO(estade): implement. |
| 401 } |
| 402 |
| 403 void AutofillDialogController::DidAcceptSuggestion(const string16& value, |
| 404 int identifier) { |
| 405 const PersonalDataManager::GUIDPair& pair = popup_guids_[identifier]; |
| 406 // TODO(estade): need to use the variant, |pair.second|. |
| 407 AutofillProfile* profile = GetManager()->GetProfileByGUID(pair.first); |
| 408 // TODO(estade): we shouldn't let this happen. |
| 409 if (!profile) |
| 410 return; |
| 411 |
| 412 // TODO(estade): implement for all sections. |
| 413 FillInputFromFormGroup(profile, &requested_billing_fields_); |
| 414 view_->UpdateSection(SECTION_BILLING); |
| 415 |
| 416 // TODO(estade): not sure why it's necessary to do this explicitly. |
| 417 popup_controller_->Hide(); |
| 418 ControllerDestroyed(); |
| 419 } |
| 420 |
| 421 void AutofillDialogController::RemoveSuggestion(int identifier) { |
| 422 // TODO(estade): implement. |
| 423 } |
| 424 |
| 425 void AutofillDialogController::RemoveAutocompleteEntry(const string16& value) { |
| 426 // TODO(estade): implement. |
| 427 } |
| 428 |
| 429 void AutofillDialogController::ClearPreviewedForm() { |
| 430 // TODO(estade): implement. |
| 431 } |
| 432 |
| 433 void AutofillDialogController::ControllerDestroyed() { |
| 434 popup_controller_ = NULL; |
| 435 } |
| 436 |
388 void AutofillDialogController::GenerateComboboxModels() { | 437 void AutofillDialogController::GenerateComboboxModels() { |
389 PersonalDataManager* manager = | 438 PersonalDataManager* manager = GetManager(); |
390 PersonalDataManagerFactory::GetForProfile(profile_); | |
391 const std::vector<CreditCard*>& cards = manager->credit_cards(); | 439 const std::vector<CreditCard*>& cards = manager->credit_cards(); |
392 for (size_t i = 0; i < cards.size(); ++i) { | 440 for (size_t i = 0; i < cards.size(); ++i) { |
393 suggested_cc_.AddItem(cards[i]->guid(), cards[i]->Label()); | 441 suggested_cc_.AddItem(cards[i]->guid(), cards[i]->Label()); |
394 } | 442 } |
395 suggested_cc_.AddItem("", ASCIIToUTF16("Enter new card")); | 443 suggested_cc_.AddItem("", ASCIIToUTF16("Enter new card")); |
396 | 444 |
397 const std::vector<AutofillProfile*>& profiles = manager->GetProfiles(); | 445 const std::vector<AutofillProfile*>& profiles = manager->GetProfiles(); |
398 const std::string app_locale = AutofillCountry::ApplicationLocale(); | 446 const std::string app_locale = AutofillCountry::ApplicationLocale(); |
399 for (size_t i = 0; i < profiles.size(); ++i) { | 447 for (size_t i = 0; i < profiles.size(); ++i) { |
400 string16 email = profiles[i]->GetInfo(EMAIL_ADDRESS, app_locale); | 448 string16 email = profiles[i]->GetInfo(EMAIL_ADDRESS, app_locale); |
401 if (!email.empty()) | 449 if (!email.empty()) |
402 suggested_email_.AddItem(profiles[i]->guid(), email); | 450 suggested_email_.AddItem(profiles[i]->guid(), email); |
403 suggested_billing_.AddItem(profiles[i]->guid(), profiles[i]->Label()); | 451 suggested_billing_.AddItem(profiles[i]->guid(), profiles[i]->Label()); |
404 suggested_shipping_.AddItem(profiles[i]->guid(), profiles[i]->Label()); | 452 suggested_shipping_.AddItem(profiles[i]->guid(), profiles[i]->Label()); |
405 } | 453 } |
406 suggested_billing_.AddItem("", ASCIIToUTF16("Enter new billing")); | 454 suggested_billing_.AddItem("", ASCIIToUTF16("Enter new billing")); |
407 suggested_email_.AddItem("", ASCIIToUTF16("Enter new email")); | 455 suggested_email_.AddItem("", ASCIIToUTF16("Enter new email")); |
408 suggested_shipping_.AddItem("", ASCIIToUTF16("Enter new shipping")); | 456 suggested_shipping_.AddItem("", ASCIIToUTF16("Enter new shipping")); |
409 } | 457 } |
410 | 458 |
411 void AutofillDialogController::PopulateInputsWithGuesses() { | |
412 PersonalDataManager* manager = | |
413 PersonalDataManagerFactory::GetForProfile(profile_); | |
414 | |
415 DetailInputs* profile_inputs[] = { &requested_email_fields_, | |
416 &requested_billing_fields_ }; | |
417 const std::vector<AutofillProfile*>& profiles = manager->GetProfiles(); | |
418 FormGroup* best_profile = | |
419 GetBestDataSource(profiles, profile_inputs, arraysize(profile_inputs)); | |
420 if (best_profile) { | |
421 FillInputsFromFormGroup(best_profile, | |
422 profile_inputs, | |
423 arraysize(profile_inputs)); | |
424 } | |
425 | |
426 DetailInputs* cc_inputs[] = { &requested_cc_fields_ }; | |
427 const std::vector<CreditCard*>& credit_cards = manager->credit_cards(); | |
428 FormGroup* best_cc = | |
429 GetBestDataSource(credit_cards, cc_inputs, arraysize(cc_inputs)); | |
430 if (best_cc) { | |
431 FillInputsFromFormGroup(best_cc, | |
432 cc_inputs, | |
433 arraysize(cc_inputs)); | |
434 } | |
435 } | |
436 | |
437 void AutofillDialogController::FillOutputForSectionWithComparator( | 459 void AutofillDialogController::FillOutputForSectionWithComparator( |
438 DialogSection section, const InputFieldComparator& compare) { | 460 DialogSection section, const InputFieldComparator& compare) { |
439 int suggestion_selection = view_->GetSuggestionSelection(section); | 461 int suggestion_selection = view_->GetSuggestionSelection(section); |
440 SuggestionsComboboxModel* model = SuggestionsModelForSection(section); | 462 SuggestionsComboboxModel* model = SuggestionsModelForSection(section); |
| 463 PersonalDataManager* manager = GetManager(); |
441 if (suggestion_selection < model->GetItemCount() - 1) { | 464 if (suggestion_selection < model->GetItemCount() - 1) { |
442 std::string guid = model->GetItemKeyAt(suggestion_selection); | 465 std::string guid = model->GetItemKeyAt(suggestion_selection); |
443 PersonalDataManager* manager = | |
444 PersonalDataManagerFactory::GetForProfile(profile_); | |
445 FormGroup* form_group = section == SECTION_CC ? | 466 FormGroup* form_group = section == SECTION_CC ? |
446 static_cast<FormGroup*>(manager->GetCreditCardByGUID(guid)) : | 467 static_cast<FormGroup*>(manager->GetCreditCardByGUID(guid)) : |
447 static_cast<FormGroup*>(manager->GetProfileByGUID(guid)); | 468 static_cast<FormGroup*>(manager->GetProfileByGUID(guid)); |
448 // TODO(estade): we shouldn't let this happen. | 469 // TODO(estade): we shouldn't let this happen. |
449 if (!form_group) | 470 if (!form_group) |
450 return; | 471 return; |
451 | 472 |
452 FillFormStructureForSection(*form_group, section, compare); | 473 FillFormStructureForSection(*form_group, section, compare); |
453 } else { | 474 } else { |
454 // The user manually input data. | 475 // The user manually input data. |
455 DetailOutputMap output; | 476 DetailOutputMap output; |
456 view_->GetUserInput(section, &output); | 477 view_->GetUserInput(section, &output); |
457 | 478 |
458 // Save the info as new or edited data, then fill it into |form_structure_|. | 479 // Save the info as new or edited data, then fill it into |form_structure_|. |
459 PersonalDataManager* manager = | |
460 PersonalDataManagerFactory::GetForProfile(profile_); | |
461 if (section == SECTION_CC) { | 480 if (section == SECTION_CC) { |
462 CreditCard card; | 481 CreditCard card; |
463 FillFormGroupFromOutputs(output, &card); | 482 FillFormGroupFromOutputs(output, &card); |
464 manager->SaveImportedCreditCard(card); | 483 manager->SaveImportedCreditCard(card); |
465 FillFormStructureForSection(card, section, compare); | 484 FillFormStructureForSection(card, section, compare); |
466 | 485 |
467 // CVC needs special-casing because the CreditCard class doesn't store | 486 // CVC needs special-casing because the CreditCard class doesn't store |
468 // or handle them. Fill it in directly from |output|. | 487 // or handle them. Fill it in directly from |output|. |
469 for (size_t i = 0; i < form_structure_.field_count(); ++i) { | 488 for (size_t i = 0; i < form_structure_.field_count(); ++i) { |
470 AutofillField* field = form_structure_.field(i); | 489 AutofillField* field = form_structure_.field(i); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
520 case SECTION_BILLING: | 539 case SECTION_BILLING: |
521 return &suggested_billing_; | 540 return &suggested_billing_; |
522 case SECTION_SHIPPING: | 541 case SECTION_SHIPPING: |
523 return &suggested_shipping_; | 542 return &suggested_shipping_; |
524 } | 543 } |
525 | 544 |
526 NOTREACHED(); | 545 NOTREACHED(); |
527 return NULL; | 546 return NULL; |
528 } | 547 } |
529 | 548 |
| 549 PersonalDataManager* AutofillDialogController::GetManager() { |
| 550 return PersonalDataManagerFactory::GetForProfile(profile_); |
| 551 } |
| 552 |
530 } // namespace autofill | 553 } // namespace autofill |
OLD | NEW |