Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/views/payments/shipping_address_editor_view_controll er.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 #include <string> | |
| 9 #include <utility> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/bind_helpers.h" | |
| 14 #include "base/location.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/single_thread_task_runner.h" | |
| 17 #include "base/strings/string16.h" | |
| 18 #include "base/strings/utf_string_conversions.h" | |
| 19 #include "base/threading/thread_task_runner_handle.h" | |
| 20 #include "chrome/browser/autofill/validation_rules_storage_factory.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h" | |
| 23 #include "chrome/browser/ui/views/payments/validating_combobox.h" | |
| 24 #include "chrome/browser/ui/views/payments/validating_textfield.h" | |
| 25 #include "chrome/grit/generated_resources.h" | |
| 26 #include "components/autofill/core/browser/autofill_address_util.h" | |
| 27 #include "components/autofill/core/browser/autofill_country.h" | |
| 28 #include "components/autofill/core/browser/autofill_type.h" | |
| 29 #include "components/autofill/core/browser/country_combobox_model.h" | |
| 30 #include "components/autofill/core/browser/field_types.h" | |
| 31 #include "components/autofill/core/browser/personal_data_manager.h" | |
| 32 #include "components/autofill/core/browser/region_combobox_model.h" | |
| 33 #include "components/autofill/core/browser/validation.h" | |
| 34 #include "components/autofill/core/common/autofill_constants.h" | |
| 35 #include "components/payments/content/payment_request.h" | |
| 36 #include "content/public/browser/web_contents.h" | |
| 37 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h" | |
| 38 #include "third_party/libaddressinput/chromium/chrome_storage_impl.h" | |
| 39 #include "third_party/libaddressinput/messages.h" | |
| 40 // TODO(mad): Bring back when we solved the double base includes issues. | |
| 41 // #include "third_party/libphonenumber/phonenumber_api.h" | |
| 42 #include "ui/base/l10n/l10n_util.h" | |
| 43 #include "ui/views/controls/textfield/textfield.h" | |
| 44 | |
| 45 // TODO(mad): Bring back when we can include libphonenumber/phonenumber_api.h. | |
| 46 // using ::i18n::phonenumbers::PhoneNumber; | |
| 47 // using ::i18n::phonenumbers::PhoneNumberUtil; | |
| 48 | |
| 49 namespace payments { | |
| 50 | |
| 51 namespace { | |
| 52 | |
| 53 const char* kAddressValidationDataURL = | |
| 54 "https://chromium-i18n.appspot.com/ssl-aggregate-address/"; | |
| 55 | |
| 56 autofill::ServerFieldType GetFieldTypeFromString(const std::string& type) { | |
| 57 if (type == autofill::kFullNameField) | |
| 58 return autofill::NAME_FULL; | |
| 59 if (type == autofill::kCompanyNameField) | |
| 60 return autofill::COMPANY_NAME; | |
| 61 if (type == autofill::kAddressLineField) | |
| 62 return autofill::ADDRESS_HOME_STREET_ADDRESS; | |
| 63 if (type == autofill::kDependentLocalityField) | |
| 64 return autofill::ADDRESS_HOME_DEPENDENT_LOCALITY; | |
| 65 if (type == autofill::kCityField) | |
| 66 return autofill::ADDRESS_HOME_CITY; | |
| 67 if (type == autofill::kStateField) | |
| 68 return autofill::ADDRESS_HOME_STATE; | |
| 69 if (type == autofill::kPostalCodeField) | |
| 70 return autofill::ADDRESS_HOME_ZIP; | |
| 71 if (type == autofill::kSortingCodeField) | |
| 72 return autofill::ADDRESS_HOME_SORTING_CODE; | |
| 73 if (type == autofill::kCountryField) | |
| 74 return autofill::ADDRESS_HOME_COUNTRY; | |
| 75 NOTREACHED(); | |
| 76 return autofill::UNKNOWN_TYPE; | |
| 77 } | |
| 78 | |
| 79 } // namespace | |
| 80 | |
| 81 ShippingAddressEditorViewController::ShippingAddressEditorViewController( | |
| 82 PaymentRequestSpec* spec, | |
| 83 PaymentRequestState* state, | |
| 84 PaymentRequestDialogView* dialog) | |
| 85 : EditorViewController(spec, state, dialog) { | |
| 86 UpdateEditorFields(); | |
| 87 } | |
| 88 | |
| 89 ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {} | |
| 90 | |
| 91 std::unique_ptr<views::View> | |
| 92 ShippingAddressEditorViewController::CreateHeaderView() { | |
| 93 return base::MakeUnique<views::View>(); | |
| 94 } | |
| 95 | |
| 96 int ShippingAddressEditorViewController::GetViewHeaderTitleId() const { | |
| 97 return IDS_PAYMENT_REQUEST_ADDRESS_EDITOR_ADD_TITLE; | |
| 98 } | |
| 99 | |
| 100 std::vector<EditorField> | |
| 101 ShippingAddressEditorViewController::GetFieldDefinitions() { | |
| 102 return editor_fields_; | |
| 103 } | |
| 104 | |
| 105 bool ShippingAddressEditorViewController::ValidateModelAndSave() { | |
| 106 autofill::AutofillProfile profile; | |
| 107 profile.set_origin(autofill::kSettingsOrigin); | |
| 108 for (const auto& field : text_fields()) { | |
| 109 // Force a blur in case the value was left untouched. | |
| 110 field.first->OnBlur(); | |
| 111 // ValidatingTextfield* is the key, EditorField is the value. | |
| 112 if (field.first->invalid()) | |
| 113 return false; | |
| 114 | |
| 115 profile.SetRawInfo(field.second.type, field.first->text()); | |
| 116 } | |
| 117 for (const auto& field : comboboxes()) { | |
| 118 // ValidatingCombobox* is the key, EditorField is the value. | |
| 119 ValidatingCombobox* combobox = field.first; | |
| 120 if (combobox->invalid()) | |
| 121 return false; | |
| 122 | |
| 123 if (combobox->id() == autofill::ADDRESS_HOME_COUNTRY) { | |
| 124 profile.SetRawInfo( | |
| 125 field.second.type, | |
| 126 base::UTF8ToUTF16(country_codes_[combobox->selected_index()])); | |
| 127 } else { | |
| 128 profile.SetRawInfo(field.second.type, | |
| 129 combobox->GetTextForRow(combobox->selected_index())); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 // Add the profile (will not add a duplicate). | |
| 134 state()->GetPersonalDataManager()->AddProfile(profile); | |
| 135 | |
| 136 return true; | |
| 137 } | |
| 138 | |
| 139 std::unique_ptr<ValidationDelegate> | |
| 140 ShippingAddressEditorViewController::CreateValidationDelegate( | |
| 141 const EditorField& field) { | |
| 142 return base::MakeUnique< | |
| 143 ShippingAddressEditorViewController::ShippingAddressValidationDelegate>( | |
| 144 this, field); | |
| 145 } | |
| 146 | |
| 147 std::unique_ptr<ui::ComboboxModel> | |
| 148 ShippingAddressEditorViewController::GetComboboxModelForType( | |
| 149 const autofill::ServerFieldType& type) { | |
| 150 switch (type) { | |
| 151 case autofill::ADDRESS_HOME_COUNTRY: { | |
| 152 autofill::CountryComboboxModel* model = | |
| 153 new autofill::CountryComboboxModel(); | |
| 154 model->SetCountries(*state()->GetPersonalDataManager(), | |
| 155 base::Callback<bool(const std::string&)>(), | |
| 156 state()->GetPersonalDataManager()->app_locale()); | |
| 157 country_codes_.clear(); | |
| 158 for (size_t i = 0; i < model->countries().size(); ++i) { | |
| 159 if (model->countries()[i].get()) | |
| 160 country_codes_.push_back(model->countries()[i]->country_code()); | |
| 161 else | |
| 162 country_codes_.push_back(""); // Separator. | |
| 163 } | |
| 164 return std::unique_ptr<ui::ComboboxModel>(model); | |
| 165 } | |
| 166 case autofill::ADDRESS_HOME_STATE: { | |
| 167 return std::unique_ptr< | |
| 168 ui::ComboboxModel>(new autofill::RegionComboboxModel( | |
| 169 base::WrapUnique(new autofill::ChromeMetadataSource( | |
| 170 kAddressValidationDataURL, | |
| 171 state()->GetPersonalDataManager()->GetURLRequestContextGetter())), | |
| 172 autofill::ValidationRulesStorageFactory::CreateStorage(), | |
| 173 state()->GetPersonalDataManager()->app_locale(), | |
| 174 country_codes_[chosen_country_index_])); | |
| 175 } | |
| 176 default: | |
| 177 NOTREACHED(); | |
| 178 break; | |
| 179 } | |
| 180 return std::unique_ptr<ui::ComboboxModel>(); | |
| 181 } | |
| 182 | |
| 183 void ShippingAddressEditorViewController::OnPerformAction( | |
| 184 views::Combobox* sender) { | |
| 185 if (sender->id() != autofill::ADDRESS_HOME_COUNTRY) | |
| 186 return; | |
| 187 if (chosen_country_index_ != sender->selected_index()) { | |
| 188 chosen_country_index_ = sender->selected_index(); | |
| 189 OnCountryChanged(sender); | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 void ShippingAddressEditorViewController::UpdateEditorView() { | |
| 194 EditorViewController::UpdateEditorView(); | |
| 195 if (chosen_country_index_ > 0) { | |
| 196 views::Combobox* country_combo_box = static_cast<views::Combobox*>( | |
| 197 dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY)); | |
| 198 DCHECK(country_combo_box); | |
| 199 country_combo_box->SetSelectedIndex(chosen_country_index_); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void ShippingAddressEditorViewController::UpdateEditorFields() { | |
| 204 editor_fields_.clear(); | |
| 205 std::unique_ptr<base::ListValue> components(new base::ListValue); | |
| 206 std::string unused; | |
| 207 std::string chosen_country_code; | |
| 208 if (chosen_country_index_ < country_codes_.size()) | |
| 209 chosen_country_code = country_codes_[chosen_country_index_]; | |
| 210 | |
| 211 autofill::GetAddressComponents( | |
| 212 chosen_country_code, state()->GetPersonalDataManager()->app_locale(), | |
| 213 components.get(), &unused); | |
| 214 | |
| 215 for (size_t lineIndex = 0; lineIndex < components->GetSize(); ++lineIndex) { | |
| 216 const base::ListValue* line = nullptr; | |
| 217 if (!components->GetList(lineIndex, &line)) { | |
| 218 NOTREACHED(); | |
| 219 return; | |
| 220 } | |
| 221 DCHECK_NE(nullptr, line); | |
| 222 for (size_t componentIndex = 0; componentIndex < line->GetSize(); | |
| 223 ++componentIndex) { | |
| 224 const base::DictionaryValue* component = nullptr; | |
| 225 if (!line->GetDictionary(componentIndex, &component)) { | |
| 226 NOTREACHED(); | |
| 227 return; | |
| 228 } | |
| 229 std::string field_type; | |
| 230 if (!component->GetString(autofill::kFieldTypeKey, &field_type)) { | |
| 231 NOTREACHED(); | |
| 232 return; | |
| 233 } | |
| 234 std::string field_name; | |
| 235 if (!component->GetString(autofill::kFieldNameKey, &field_name)) { | |
| 236 NOTREACHED(); | |
| 237 return; | |
| 238 } | |
| 239 std::string field_length; | |
| 240 if (!component->GetString(autofill::kFieldLengthKey, &field_length)) { | |
| 241 NOTREACHED(); | |
| 242 return; | |
| 243 } | |
| 244 EditorField::LengthHint length_hint = EditorField::LengthHint::HINT_LONG; | |
| 245 if (field_length == autofill::kShortField) | |
| 246 length_hint = EditorField::LengthHint::HINT_SHORT; | |
| 247 else | |
| 248 DCHECK_EQ(autofill::kLongField, field_length); | |
| 249 autofill::ServerFieldType server_field_type = | |
| 250 GetFieldTypeFromString(field_type); | |
| 251 EditorField::ControlType control_type = | |
| 252 EditorField::ControlType::TEXTFIELD; | |
| 253 if (server_field_type == autofill::ADDRESS_HOME_COUNTRY || | |
| 254 server_field_type == autofill::ADDRESS_HOME_STATE) { | |
| 255 control_type = EditorField::ControlType::COMBOBOX; | |
| 256 } | |
| 257 editor_fields_.push_back(EditorField( | |
| 258 server_field_type, base::UTF8ToUTF16(field_name), length_hint, | |
| 259 server_field_type != autofill::COMPANY_NAME, // required. | |
| 260 control_type)); | |
| 261 // Insert the Country combo box right after NAME_FULL. | |
| 262 if (server_field_type == autofill::NAME_FULL) { | |
| 263 editor_fields_.push_back( | |
| 264 EditorField(autofill::ADDRESS_HOME_COUNTRY, | |
| 265 l10n_util::GetStringUTF16( | |
| 266 IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL), | |
| 267 EditorField::LengthHint::HINT_LONG, true, | |
| 268 EditorField::ControlType::COMBOBOX)); | |
| 269 } | |
| 270 } | |
| 271 } | |
| 272 // Always add phone number at the end. | |
| 273 editor_fields_.push_back( | |
| 274 EditorField(autofill::PHONE_HOME_NUMBER, | |
| 275 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE), | |
| 276 EditorField::LengthHint::HINT_SHORT, false, // required. | |
|
Mathieu
2017/03/21 17:10:15
Let's make the phone field LONG
MAD
2017/03/21 19:28:34
Done.
| |
| 277 EditorField::ControlType::TEXTFIELD)); | |
| 278 } | |
| 279 | |
| 280 void ShippingAddressEditorViewController::OnCountryChanged( | |
| 281 views::Combobox* combobox) { | |
| 282 // Make sure to unfocus the combo box so we can remove it when we update the | |
| 283 // view. | |
| 284 combobox->SetFocusBehavior(views::View::FocusBehavior::NEVER); | |
| 285 UpdateEditorFields(); | |
| 286 // Let the focus change complete before updating the editor. | |
| 287 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 288 FROM_HERE, | |
| 289 base::Bind(&ShippingAddressEditorViewController::UpdateEditorView, | |
| 290 base::Unretained(this))); | |
| 291 } | |
| 292 | |
| 293 ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 294 ShippingAddressValidationDelegate( | |
| 295 ShippingAddressEditorViewController* parent, | |
| 296 const EditorField& field) | |
| 297 : field_(field) /* TODO(mad): Bring back when needed:, parent_(parent) */ {} | |
| 298 | |
| 299 ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 300 ~ShippingAddressValidationDelegate() {} | |
| 301 | |
| 302 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 303 ValidateTextfield(views::Textfield* textfield) { | |
| 304 return ValidateValue(textfield->text()); | |
| 305 } | |
| 306 | |
| 307 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 308 ValidateCombobox(views::Combobox* combobox) { | |
| 309 return ValidateValue(combobox->GetTextForRow(combobox->selected_index())); | |
| 310 } | |
| 311 | |
| 312 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 313 ValidateValue(const base::string16& value) { | |
| 314 /* | |
| 315 TODO(mad): Bring back when we can include libphonenumber/phonenumber_api.h. | |
| 316 if (field_.type == autofill::PHONE_HOME_NUMBER) { | |
| 317 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); | |
| 318 PhoneNumber number_object; | |
| 319 return phone_util->Parse( | |
| 320 base::UTF16ToUTF8(value), | |
| 321 parent_->country_codes_[parent_->chosen_country_index_], | |
| 322 &number_object) == PhoneNumberUtil::NO_PARSING_ERROR; | |
| 323 | |
| 324 }*/ | |
| 325 // TODO(mathp): Display "required" error if applicable. | |
| 326 return !field_.required || !value.empty(); | |
| 327 } | |
| 328 | |
| 329 } // namespace payments | |
| OLD | NEW |