Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc

Issue 2883333003: Payment request shipping address editor now properly handles countries (Closed)
Patch Set: Final nits Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 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/views/payments/shipping_address_editor_view_controll er.h" 5 #include "chrome/browser/ui/views/payments/shipping_address_editor_view_controll er.h"
6 6
7 #include <utility>
8
9 #include "base/bind.h" 7 #include "base/bind.h"
10 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
11 #include "base/callback.h" 9 #include "base/callback.h"
12 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
13 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread_task_runner_handle.h" 12 #include "base/threading/thread_task_runner_handle.h"
15 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h" 13 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
16 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h" 14 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
17 #include "chrome/browser/ui/views/payments/validating_combobox.h" 15 #include "chrome/browser/ui/views/payments/validating_combobox.h"
18 #include "chrome/browser/ui/views/payments/validating_textfield.h" 16 #include "chrome/browser/ui/views/payments/validating_textfield.h"
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 if (type == autofill::kPostalCodeField) 54 if (type == autofill::kPostalCodeField)
57 return autofill::ADDRESS_HOME_ZIP; 55 return autofill::ADDRESS_HOME_ZIP;
58 if (type == autofill::kSortingCodeField) 56 if (type == autofill::kSortingCodeField)
59 return autofill::ADDRESS_HOME_SORTING_CODE; 57 return autofill::ADDRESS_HOME_SORTING_CODE;
60 if (type == autofill::kCountryField) 58 if (type == autofill::kCountryField)
61 return autofill::ADDRESS_HOME_COUNTRY; 59 return autofill::ADDRESS_HOME_COUNTRY;
62 NOTREACHED(); 60 NOTREACHED();
63 return autofill::UNKNOWN_TYPE; 61 return autofill::UNKNOWN_TYPE;
64 } 62 }
65 63
64 // size_t doesn't have a defined maximum value, so this is a trick to create one
65 // as is done for std::string::npos.
66 // http://www.cplusplus.com/reference/string/string/npos
67 const size_t kInvalidCountryIndex = static_cast<size_t>(-1);
68
66 } // namespace 69 } // namespace
67 70
68 ShippingAddressEditorViewController::ShippingAddressEditorViewController( 71 ShippingAddressEditorViewController::ShippingAddressEditorViewController(
69 PaymentRequestSpec* spec, 72 PaymentRequestSpec* spec,
70 PaymentRequestState* state, 73 PaymentRequestState* state,
71 PaymentRequestDialogView* dialog, 74 PaymentRequestDialogView* dialog,
72 BackNavigationType back_navigation_type, 75 BackNavigationType back_navigation_type,
73 base::OnceClosure on_edited, 76 base::OnceClosure on_edited,
74 base::OnceCallback<void(const autofill::AutofillProfile&)> on_added, 77 base::OnceCallback<void(const autofill::AutofillProfile&)> on_added,
75 autofill::AutofillProfile* profile) 78 autofill::AutofillProfile* profile)
76 : EditorViewController(spec, state, dialog, back_navigation_type), 79 : EditorViewController(spec, state, dialog, back_navigation_type),
77 on_edited_(std::move(on_edited)), 80 on_edited_(std::move(on_edited)),
78 on_added_(std::move(on_added)), 81 on_added_(std::move(on_added)),
79 profile_to_edit_(profile), 82 profile_to_edit_(profile),
80 chosen_country_index_(0), 83 chosen_country_index_(kInvalidCountryIndex),
81 failed_to_load_region_data_(false) { 84 failed_to_load_region_data_(false) {
85 UpdateCountries(/*model=*/nullptr);
82 UpdateEditorFields(); 86 UpdateEditorFields();
83 } 87 }
84 88
85 ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {} 89 ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {}
86 90
87 std::vector<EditorField> 91 std::vector<EditorField>
88 ShippingAddressEditorViewController::GetFieldDefinitions() { 92 ShippingAddressEditorViewController::GetFieldDefinitions() {
89 return editor_fields_; 93 return editor_fields_;
90 } 94 }
91 95
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 std::unique_ptr<ui::ComboboxModel> 148 std::unique_ptr<ui::ComboboxModel>
145 ShippingAddressEditorViewController::GetComboboxModelForType( 149 ShippingAddressEditorViewController::GetComboboxModelForType(
146 const autofill::ServerFieldType& type) { 150 const autofill::ServerFieldType& type) {
147 switch (type) { 151 switch (type) {
148 case autofill::ADDRESS_HOME_COUNTRY: { 152 case autofill::ADDRESS_HOME_COUNTRY: {
149 std::unique_ptr<autofill::CountryComboboxModel> model = 153 std::unique_ptr<autofill::CountryComboboxModel> model =
150 base::MakeUnique<autofill::CountryComboboxModel>(); 154 base::MakeUnique<autofill::CountryComboboxModel>();
151 model->SetCountries(*state()->GetPersonalDataManager(), 155 model->SetCountries(*state()->GetPersonalDataManager(),
152 base::Callback<bool(const std::string&)>(), 156 base::Callback<bool(const std::string&)>(),
153 state()->GetApplicationLocale()); 157 state()->GetApplicationLocale());
154 country_codes_.clear(); 158 if (model->countries().size() != countries_.size())
155 for (size_t i = 0; i < model->countries().size(); ++i) { 159 UpdateCountries(model.get());
156 if (model->countries()[i].get())
157 country_codes_.push_back(model->countries()[i]->country_code());
158 else
159 country_codes_.push_back(""); // Separator.
160 }
161 return std::move(model); 160 return std::move(model);
162 } 161 }
163 case autofill::ADDRESS_HOME_STATE: { 162 case autofill::ADDRESS_HOME_STATE: {
164 std::unique_ptr<autofill::RegionComboboxModel> model = 163 std::unique_ptr<autofill::RegionComboboxModel> model =
165 base::MakeUnique<autofill::RegionComboboxModel>(); 164 base::MakeUnique<autofill::RegionComboboxModel>();
166 model->LoadRegionData(country_codes_[chosen_country_index_], 165 if (chosen_country_index_ < countries_.size()) {
167 state()->GetRegionDataLoader(), 166 model->LoadRegionData(countries_[chosen_country_index_].first,
168 /*timeout_ms=*/5000); 167 state()->GetRegionDataLoader(),
169 if (!model->IsPendingRegionDataLoad()) { 168 /*timeout_ms=*/5000);
170 // If the data was already pre-loaded, the observer won't get notified 169 if (!model->IsPendingRegionDataLoad()) {
171 // so we have to check for failure here. 170 // If the data was already pre-loaded, the observer won't get notified
172 failed_to_load_region_data_ = model->failed_to_load_data(); 171 // so we have to check for failure here.
173 if (failed_to_load_region_data_) { 172 failed_to_load_region_data_ = model->failed_to_load_data();
174 // We can update the view synchronously while building the view.
175 OnDataChanged(/*synchronous=*/false);
176 } 173 }
174 } else {
175 failed_to_load_region_data_ = true;
176 }
177 if (failed_to_load_region_data_) {
178 // We can't update the view synchronously while building the view.
179 OnDataChanged(/*synchronous=*/false);
177 } 180 }
178 return std::move(model); 181 return std::move(model);
179 } 182 }
180 default: 183 default:
181 NOTREACHED(); 184 NOTREACHED();
182 break; 185 break;
183 } 186 }
184 return std::unique_ptr<ui::ComboboxModel>(); 187 return std::unique_ptr<ui::ComboboxModel>();
185 } 188 }
186 189
187 void ShippingAddressEditorViewController::OnPerformAction( 190 void ShippingAddressEditorViewController::OnPerformAction(
188 views::Combobox* sender) { 191 views::Combobox* sender) {
189 EditorViewController::OnPerformAction(sender); 192 EditorViewController::OnPerformAction(sender);
190 if (sender->id() != autofill::ADDRESS_HOME_COUNTRY) 193 if (sender->id() != autofill::ADDRESS_HOME_COUNTRY)
191 return; 194 return;
192 DCHECK_GE(sender->selected_index(), 0); 195 DCHECK_GE(sender->selected_index(), 0);
193 if (chosen_country_index_ != static_cast<size_t>(sender->selected_index())) { 196 if (chosen_country_index_ != static_cast<size_t>(sender->selected_index())) {
194 chosen_country_index_ = sender->selected_index(); 197 chosen_country_index_ = sender->selected_index();
195 failed_to_load_region_data_ = false; 198 failed_to_load_region_data_ = false;
196 // View update must be asynchronous to let the combobox finish performing 199 // View update must be asynchronous to let the combobox finish performing
197 // the action. 200 // the action.
198 OnDataChanged(/*synchronous=*/false); 201 OnDataChanged(/*synchronous=*/false);
199 } 202 }
200 } 203 }
201 204
202 void ShippingAddressEditorViewController::UpdateEditorView() { 205 void ShippingAddressEditorViewController::UpdateEditorView() {
203 EditorViewController::UpdateEditorView(); 206 EditorViewController::UpdateEditorView();
204 if (chosen_country_index_ > 0UL) { 207 if (chosen_country_index_ > 0UL &&
208 chosen_country_index_ < countries_.size()) {
205 views::Combobox* country_combo_box = static_cast<views::Combobox*>( 209 views::Combobox* country_combo_box = static_cast<views::Combobox*>(
206 dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY)); 210 dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY));
207 DCHECK(country_combo_box); 211 DCHECK(country_combo_box);
212 DCHECK_EQ(countries_.size(),
213 static_cast<size_t>(country_combo_box->GetRowCount()));
208 country_combo_box->SetSelectedIndex(chosen_country_index_); 214 country_combo_box->SetSelectedIndex(chosen_country_index_);
215 } else if (countries_.size() > 0UL) {
216 chosen_country_index_ = 0UL;
217 } else {
218 chosen_country_index_ = kInvalidCountryIndex;
209 } 219 }
210 // Ignore temporary profile once the editor view has been updated. 220 // Ignore temporary profile once the editor view has been updated.
211 temporary_profile_.reset(nullptr); 221 temporary_profile_.reset();
212 } 222 }
213 223
214 base::string16 ShippingAddressEditorViewController::GetSheetTitle() { 224 base::string16 ShippingAddressEditorViewController::GetSheetTitle() {
215 // TODO(crbug.com/712074): Editor title should reflect the missing information 225 // TODO(crbug.com/712074): Editor title should reflect the missing information
216 // in the case that one or more fields are missing. 226 // in the case that one or more fields are missing.
217 return profile_to_edit_ ? l10n_util::GetStringUTF16(IDS_PAYMENTS_EDIT_ADDRESS) 227 return profile_to_edit_ ? l10n_util::GetStringUTF16(IDS_PAYMENTS_EDIT_ADDRESS)
218 : l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_ADDRESS); 228 : l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_ADDRESS);
219 } 229 }
220 230
221 std::unique_ptr<views::Button> 231 std::unique_ptr<views::Button>
222 ShippingAddressEditorViewController::CreatePrimaryButton() { 232 ShippingAddressEditorViewController::CreatePrimaryButton() {
223 std::unique_ptr<views::Button> button( 233 std::unique_ptr<views::Button> button(
224 EditorViewController::CreatePrimaryButton()); 234 EditorViewController::CreatePrimaryButton());
225 button->set_id(static_cast<int>(DialogViewID::SAVE_ADDRESS_BUTTON)); 235 button->set_id(static_cast<int>(DialogViewID::SAVE_ADDRESS_BUTTON));
226 return button; 236 return button;
227 } 237 }
228 238
239 void ShippingAddressEditorViewController::UpdateCountries(
240 autofill::CountryComboboxModel* model) {
241 autofill::CountryComboboxModel local_model;
242 if (!model) {
243 local_model.SetCountries(*state()->GetPersonalDataManager(),
244 base::Callback<bool(const std::string&)>(),
245 state()->GetApplicationLocale());
246 model = &local_model;
247 }
248
249 for (size_t i = 0; i < model->countries().size(); ++i) {
250 autofill::AutofillCountry* country(model->countries()[i].get());
251 if (country) {
252 countries_.push_back(
253 std::make_pair(country->country_code(), country->name()));
254 } else {
255 // Separator, kept to make sure the size of the vector stays the same.
256 countries_.push_back(std::make_pair("", base::UTF8ToUTF16("")));
257 }
258 }
259 // If there is a profile to edit, make sure to use its country for the initial
260 // |chosen_country_index_|.
261 if (profile_to_edit_) {
262 autofill::AutofillType country_type(autofill::ADDRESS_HOME_COUNTRY);
263 base::string16 chosen_country(profile_to_edit_->GetInfo(
264 country_type, state()->GetApplicationLocale()));
265 for (chosen_country_index_ = 0; chosen_country_index_ < countries_.size();
266 ++chosen_country_index_) {
267 if (chosen_country == countries_[chosen_country_index_].second)
268 break;
269 }
270 // Make sure the the country was actually found in |countries_|, otherwise
271 // set |chosen_country_index_| as the default country at index 0.
272 if (chosen_country_index_ >= countries_.size()) {
273 // But only if there is at least one country.
274 if (countries_.size() > 0) {
275 LOG(ERROR) << "Unexpected country: " << chosen_country;
276 chosen_country_index_ = 0;
277 profile_to_edit_->SetInfo(country_type,
278 countries_[chosen_country_index_].second,
279 state()->GetApplicationLocale());
280 } else {
281 LOG(ERROR) << "Unexpected empty country list!";
282 chosen_country_index_ = kInvalidCountryIndex;
283 }
284 }
285 } else if (countries_.size() > 0) {
286 chosen_country_index_ = 0;
287 }
288 }
289
229 void ShippingAddressEditorViewController::UpdateEditorFields() { 290 void ShippingAddressEditorViewController::UpdateEditorFields() {
230 editor_fields_.clear(); 291 editor_fields_.clear();
231 std::string chosen_country_code; 292 std::string chosen_country_code;
232 if (chosen_country_index_ < country_codes_.size()) 293 if (chosen_country_index_ < countries_.size())
233 chosen_country_code = country_codes_[chosen_country_index_]; 294 chosen_country_code = countries_[chosen_country_index_].first;
234 295
235 std::unique_ptr<base::ListValue> components(new base::ListValue); 296 std::unique_ptr<base::ListValue> components(new base::ListValue);
236 std::string unused; 297 std::string unused;
237 autofill::GetAddressComponents(chosen_country_code, 298 autofill::GetAddressComponents(chosen_country_code,
238 state()->GetApplicationLocale(), 299 state()->GetApplicationLocale(),
239 components.get(), &unused); 300 components.get(), &unused);
240 for (size_t line_index = 0; line_index < components->GetSize(); 301 for (size_t line_index = 0; line_index < components->GetSize();
241 ++line_index) { 302 ++line_index) {
242 const base::ListValue* line = nullptr; 303 const base::ListValue* line = nullptr;
243 if (!components->GetList(line_index, &line)) { 304 if (!components->GetList(line_index, &line)) {
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 FROM_HERE, 377 FROM_HERE,
317 base::BindOnce(&ShippingAddressEditorViewController::UpdateEditorView, 378 base::BindOnce(&ShippingAddressEditorViewController::UpdateEditorView,
318 base::Unretained(this))); 379 base::Unretained(this)));
319 } 380 }
320 } 381 }
321 382
322 bool ShippingAddressEditorViewController::SaveFieldsToProfile( 383 bool ShippingAddressEditorViewController::SaveFieldsToProfile(
323 autofill::AutofillProfile* profile, 384 autofill::AutofillProfile* profile,
324 bool ignore_errors) { 385 bool ignore_errors) {
325 const std::string& locale = state()->GetApplicationLocale(); 386 const std::string& locale = state()->GetApplicationLocale();
387 // The country must be set first, because the profile uses the country to
388 // interpret some of the data (e.g., phone numbers) passed to SetInfo.
389 views::Combobox* combobox = static_cast<views::Combobox*>(
390 dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY));
391 // The combobox can be null when saving to temporary profile while updating
392 // the view.
393 if (combobox) {
394 base::string16 country(combobox->GetTextForRow(combobox->selected_index()));
395 bool success =
396 profile->SetInfo(autofill::AutofillType(autofill::ADDRESS_HOME_COUNTRY),
397 country, locale);
398 LOG_IF(ERROR, !success && !ignore_errors)
399 << "Can't set profile country to: " << country;
400 if (!success && !ignore_errors)
401 return false;
402 } else {
403 DCHECK_EQ(temporary_profile_.get(), profile);
404 }
405
326 bool success = true; 406 bool success = true;
327 for (const auto& field : text_fields()) { 407 for (const auto& field : text_fields()) {
328 // Force a blur in case the value was left untouched. 408 // Force a blur in case the value was left untouched.
329 field.first->OnBlur(); 409 field.first->OnBlur();
330 // ValidatingTextfield* is the key, EditorField is the value. 410 // ValidatingTextfield* is the key, EditorField is the value.
331 if (field.first->invalid()) { 411 if (field.first->invalid()) {
332 success = false; 412 success = false;
333 } else { 413 } else {
334 success = profile->SetInfo(autofill::AutofillType(field.second.type), 414 success = profile->SetInfo(autofill::AutofillType(field.second.type),
335 field.first->text(), locale); 415 field.first->text(), locale);
336 } 416 }
337 LOG_IF(ERROR, !success && !ignore_errors) 417 LOG_IF(ERROR, !success && !ignore_errors)
338 << "Can't setinfo(" << field.second.type << ", " << field.first->text(); 418 << "Can't setinfo(" << field.second.type << ", " << field.first->text();
339 if (!success && !ignore_errors) 419 if (!success && !ignore_errors)
340 return false; 420 return false;
341 } 421 }
342 for (const auto& field : comboboxes()) { 422 for (const auto& field : comboboxes()) {
343 // ValidatingCombobox* is the key, EditorField is the value. 423 // ValidatingCombobox* is the key, EditorField is the value.
344 ValidatingCombobox* combobox = field.first; 424 ValidatingCombobox* combobox = field.first;
425 // The country has already been dealt with.
426 if (combobox->id() == autofill::ADDRESS_HOME_COUNTRY)
427 continue;
345 if (combobox->invalid()) { 428 if (combobox->invalid()) {
346 success = false; 429 success = false;
347 } else { 430 } else {
348 if (combobox->id() == autofill::ADDRESS_HOME_COUNTRY) { 431 success = profile->SetInfo(
349 success = profile->SetInfo( 432 autofill::AutofillType(field.second.type),
350 autofill::AutofillType(field.second.type), 433 combobox->GetTextForRow(combobox->selected_index()), locale);
351 base::UTF8ToUTF16(country_codes_[combobox->selected_index()]),
352 locale);
353 } else {
354 success = profile->SetInfo(
355 autofill::AutofillType(field.second.type),
356 combobox->GetTextForRow(combobox->selected_index()), locale);
357 }
358 } 434 }
359 LOG_IF(ERROR, !success && !ignore_errors) 435 LOG_IF(ERROR, !success && !ignore_errors)
360 << "Can't setinfo(" << field.second.type << ", " 436 << "Can't setinfo(" << field.second.type << ", "
361 << combobox->GetTextForRow(combobox->selected_index()); 437 << combobox->GetTextForRow(combobox->selected_index());
362 if (!success && !ignore_errors) 438 if (!success && !ignore_errors)
363 return false; 439 return false;
364 } 440 }
365 return success; 441 return success;
366 } 442 }
367 443
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
407 483
408 void ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: 484 void ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
409 ComboboxModelChanged(views::Combobox* combobox) { 485 ComboboxModelChanged(views::Combobox* combobox) {
410 controller_->OnComboboxModelChanged(combobox); 486 controller_->OnComboboxModelChanged(combobox);
411 } 487 }
412 488
413 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: 489 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
414 ValidateValue(const base::string16& value) { 490 ValidateValue(const base::string16& value) {
415 if (!value.empty()) { 491 if (!value.empty()) {
416 if (field_.type == autofill::PHONE_HOME_WHOLE_NUMBER && 492 if (field_.type == autofill::PHONE_HOME_WHOLE_NUMBER &&
493 controller_->chosen_country_index_ < controller_->countries_.size() &&
417 !autofill::IsValidPhoneNumber( 494 !autofill::IsValidPhoneNumber(
418 value, 495 value, controller_->countries_[controller_->chosen_country_index_]
419 controller_->country_codes_[controller_->chosen_country_index_])) { 496 .first)) {
420 controller_->DisplayErrorMessageForField( 497 controller_->DisplayErrorMessageForField(
421 field_, l10n_util::GetStringUTF16( 498 field_, l10n_util::GetStringUTF16(
422 IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE)); 499 IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE));
423 return false; 500 return false;
424 } 501 }
425 // As long as other field types are non-empty, they are valid. 502 // As long as other field types are non-empty, they are valid.
426 controller_->DisplayErrorMessageForField(field_, base::ASCIIToUTF16("")); 503 controller_->DisplayErrorMessageForField(field_, base::ASCIIToUTF16(""));
427 return true; 504 return true;
428 } 505 }
429 bool is_required_valid = !field_.required; 506 bool is_required_valid = !field_.required;
430 const base::string16 displayed_message = 507 const base::string16 displayed_message =
431 is_required_valid ? base::ASCIIToUTF16("") 508 is_required_valid ? base::ASCIIToUTF16("")
432 : l10n_util::GetStringUTF16( 509 : l10n_util::GetStringUTF16(
433 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE); 510 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
434 controller_->DisplayErrorMessageForField(field_, displayed_message); 511 controller_->DisplayErrorMessageForField(field_, displayed_message);
435 return is_required_valid; 512 return is_required_valid;
436 } 513 }
437 514
438 } // namespace payments 515 } // namespace payments
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698