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_state.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 #include "ui/base/l10n/l10n_util.h" | |
| 41 #include "ui/views/controls/textfield/textfield.h" | |
| 42 | |
| 43 namespace payments { | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 47 // Converts a field type in string format as returned by | |
| 48 // autofill::GetAddressComponents into the appropriate autofill::ServerFieldType | |
| 49 // enum. | |
| 50 autofill::ServerFieldType GetFieldTypeFromString(const std::string& type) { | |
| 51 if (type == autofill::kFullNameField) | |
| 52 return autofill::NAME_FULL; | |
| 53 if (type == autofill::kCompanyNameField) | |
| 54 return autofill::COMPANY_NAME; | |
| 55 if (type == autofill::kAddressLineField) | |
| 56 return autofill::ADDRESS_HOME_STREET_ADDRESS; | |
| 57 if (type == autofill::kDependentLocalityField) | |
| 58 return autofill::ADDRESS_HOME_DEPENDENT_LOCALITY; | |
| 59 if (type == autofill::kCityField) | |
| 60 return autofill::ADDRESS_HOME_CITY; | |
| 61 if (type == autofill::kStateField) | |
| 62 return autofill::ADDRESS_HOME_STATE; | |
| 63 if (type == autofill::kPostalCodeField) | |
| 64 return autofill::ADDRESS_HOME_ZIP; | |
| 65 if (type == autofill::kSortingCodeField) | |
| 66 return autofill::ADDRESS_HOME_SORTING_CODE; | |
| 67 if (type == autofill::kCountryField) | |
| 68 return autofill::ADDRESS_HOME_COUNTRY; | |
| 69 NOTREACHED(); | |
| 70 return autofill::UNKNOWN_TYPE; | |
| 71 } | |
| 72 | |
| 73 } // namespace | |
| 74 | |
| 75 ShippingAddressEditorViewController::ShippingAddressEditorViewController( | |
| 76 PaymentRequestSpec* spec, | |
| 77 PaymentRequestState* state, | |
| 78 PaymentRequestDialogView* dialog) | |
| 79 : EditorViewController(spec, state, dialog) { | |
| 80 UpdateEditorFields(); | |
| 81 } | |
| 82 | |
| 83 ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {} | |
| 84 | |
| 85 std::unique_ptr<views::View> | |
| 86 ShippingAddressEditorViewController::CreateHeaderView() { | |
| 87 return base::MakeUnique<views::View>(); | |
| 88 } | |
| 89 | |
| 90 int ShippingAddressEditorViewController::GetViewHeaderTitleId() const { | |
| 91 return IDS_PAYMENT_REQUEST_ADDRESS_EDITOR_ADD_TITLE; | |
| 92 } | |
| 93 | |
| 94 std::vector<EditorField> | |
| 95 ShippingAddressEditorViewController::GetFieldDefinitions() { | |
| 96 return editor_fields_; | |
| 97 } | |
| 98 | |
| 99 bool ShippingAddressEditorViewController::ValidateModelAndSave() { | |
| 100 autofill::AutofillProfile profile; | |
| 101 profile.set_origin(autofill::kSettingsOrigin); | |
| 102 for (const auto& field : text_fields()) { | |
| 103 // Force a blur in case the value was left untouched. | |
| 104 field.first->OnBlur(); | |
| 105 // ValidatingTextfield* is the key, EditorField is the value. | |
| 106 if (field.first->invalid()) | |
| 107 return false; | |
| 108 | |
| 109 profile.SetRawInfo(field.second.type, field.first->text()); | |
| 110 } | |
| 111 for (const auto& field : comboboxes()) { | |
| 112 // ValidatingCombobox* is the key, EditorField is the value. | |
| 113 ValidatingCombobox* combobox = field.first; | |
| 114 if (combobox->invalid()) | |
| 115 return false; | |
| 116 | |
| 117 if (combobox->id() == autofill::ADDRESS_HOME_COUNTRY) { | |
| 118 profile.SetRawInfo( | |
| 119 field.second.type, | |
| 120 base::UTF8ToUTF16(country_codes_[combobox->selected_index()])); | |
| 121 } else { | |
| 122 profile.SetRawInfo(field.second.type, | |
| 123 combobox->GetTextForRow(combobox->selected_index())); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 // Add the profile (will not add a duplicate). | |
| 128 state()->GetPersonalDataManager()->AddProfile(profile); | |
| 129 | |
| 130 return true; | |
| 131 } | |
| 132 | |
| 133 std::unique_ptr<ValidationDelegate> | |
| 134 ShippingAddressEditorViewController::CreateValidationDelegate( | |
| 135 const EditorField& field) { | |
| 136 return base::MakeUnique< | |
| 137 ShippingAddressEditorViewController::ShippingAddressValidationDelegate>( | |
| 138 this, field); | |
| 139 } | |
| 140 | |
| 141 std::unique_ptr<ui::ComboboxModel> | |
| 142 ShippingAddressEditorViewController::GetComboboxModelForType( | |
| 143 const autofill::ServerFieldType& type) { | |
| 144 switch (type) { | |
| 145 case autofill::ADDRESS_HOME_COUNTRY: { | |
| 146 autofill::CountryComboboxModel* model = | |
|
anthonyvd
2017/03/21 21:18:24
Same comment as above about using unique_ptr and .
MAD
2017/03/22 20:15:42
Done.
| |
| 147 new autofill::CountryComboboxModel(); | |
| 148 model->SetCountries(*state()->GetPersonalDataManager(), | |
| 149 base::Callback<bool(const std::string&)>(), | |
| 150 state()->GetApplicationLocale()); | |
| 151 country_codes_.clear(); | |
| 152 for (size_t i = 0; i < model->countries().size(); ++i) { | |
| 153 if (model->countries()[i].get()) | |
| 154 country_codes_.push_back(model->countries()[i]->country_code()); | |
| 155 else | |
| 156 country_codes_.push_back(""); // Separator. | |
| 157 } | |
| 158 return std::unique_ptr<ui::ComboboxModel>(model); | |
| 159 } | |
| 160 case autofill::ADDRESS_HOME_STATE: { | |
| 161 return std::unique_ptr< | |
| 162 ui::ComboboxModel>(new autofill::RegionComboboxModel( | |
| 163 base::WrapUnique(new autofill::ChromeMetadataSource( | |
| 164 I18N_ADDRESS_VALIDATION_DATA_URL, | |
| 165 state()->GetPersonalDataManager()->GetURLRequestContextGetter())), | |
| 166 autofill::ValidationRulesStorageFactory::CreateStorage(), | |
| 167 state()->GetApplicationLocale(), | |
| 168 country_codes_[chosen_country_index_])); | |
| 169 } | |
| 170 default: | |
| 171 NOTREACHED(); | |
| 172 break; | |
| 173 } | |
| 174 return std::unique_ptr<ui::ComboboxModel>(); | |
| 175 } | |
| 176 | |
| 177 void ShippingAddressEditorViewController::OnPerformAction( | |
| 178 views::Combobox* sender) { | |
| 179 if (sender->id() != autofill::ADDRESS_HOME_COUNTRY) | |
| 180 return; | |
| 181 DCHECK_GE(sender->selected_index(), 0); | |
| 182 if (chosen_country_index_ != static_cast<size_t>(sender->selected_index())) { | |
| 183 chosen_country_index_ = sender->selected_index(); | |
| 184 OnCountryChanged(sender); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 void ShippingAddressEditorViewController::UpdateEditorView() { | |
| 189 EditorViewController::UpdateEditorView(); | |
| 190 if (chosen_country_index_ > 0UL) { | |
| 191 views::Combobox* country_combo_box = static_cast<views::Combobox*>( | |
| 192 dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY)); | |
| 193 DCHECK(country_combo_box); | |
| 194 country_combo_box->SetSelectedIndex(chosen_country_index_); | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 void ShippingAddressEditorViewController::UpdateEditorFields() { | |
| 199 editor_fields_.clear(); | |
| 200 std::string chosen_country_code; | |
| 201 if (chosen_country_index_ < country_codes_.size()) | |
| 202 chosen_country_code = country_codes_[chosen_country_index_]; | |
| 203 | |
| 204 std::unique_ptr<base::ListValue> components(new base::ListValue); | |
| 205 std::string unused; | |
| 206 autofill::GetAddressComponents(chosen_country_code, | |
| 207 state()->GetApplicationLocale(), | |
| 208 components.get(), &unused); | |
| 209 | |
| 210 for (size_t line_index = 0; line_index < components->GetSize(); | |
| 211 ++line_index) { | |
| 212 const base::ListValue* line = nullptr; | |
| 213 if (!components->GetList(line_index, &line)) { | |
| 214 NOTREACHED(); | |
| 215 return; | |
| 216 } | |
| 217 DCHECK_NE(nullptr, line); | |
| 218 for (size_t component_index = 0; component_index < line->GetSize(); | |
| 219 ++component_index) { | |
| 220 const base::DictionaryValue* component = nullptr; | |
| 221 if (!line->GetDictionary(component_index, &component)) { | |
| 222 NOTREACHED(); | |
| 223 return; | |
| 224 } | |
| 225 std::string field_type; | |
| 226 if (!component->GetString(autofill::kFieldTypeKey, &field_type)) { | |
| 227 NOTREACHED(); | |
| 228 return; | |
| 229 } | |
| 230 std::string field_name; | |
| 231 if (!component->GetString(autofill::kFieldNameKey, &field_name)) { | |
| 232 NOTREACHED(); | |
| 233 return; | |
| 234 } | |
| 235 std::string field_length; | |
| 236 if (!component->GetString(autofill::kFieldLengthKey, &field_length)) { | |
| 237 NOTREACHED(); | |
| 238 return; | |
| 239 } | |
| 240 EditorField::LengthHint length_hint = EditorField::LengthHint::HINT_LONG; | |
| 241 if (field_length == autofill::kShortField) | |
| 242 length_hint = EditorField::LengthHint::HINT_SHORT; | |
| 243 else | |
| 244 DCHECK_EQ(autofill::kLongField, field_length); | |
| 245 autofill::ServerFieldType server_field_type = | |
| 246 GetFieldTypeFromString(field_type); | |
| 247 EditorField::ControlType control_type = | |
| 248 EditorField::ControlType::TEXTFIELD; | |
| 249 if (server_field_type == autofill::ADDRESS_HOME_COUNTRY || | |
| 250 server_field_type == autofill::ADDRESS_HOME_STATE) { | |
| 251 control_type = EditorField::ControlType::COMBOBOX; | |
| 252 } | |
| 253 editor_fields_.emplace_back( | |
| 254 server_field_type, base::UTF8ToUTF16(field_name), length_hint, | |
| 255 /*required=*/server_field_type != autofill::COMPANY_NAME, | |
| 256 control_type); | |
| 257 // Insert the Country combobox right after NAME_FULL. | |
| 258 if (server_field_type == autofill::NAME_FULL) { | |
| 259 editor_fields_.emplace_back( | |
| 260 autofill::ADDRESS_HOME_COUNTRY, | |
| 261 l10n_util::GetStringUTF16( | |
| 262 IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL), | |
| 263 EditorField::LengthHint::HINT_LONG, /*required=*/true, | |
| 264 EditorField::ControlType::COMBOBOX); | |
| 265 } | |
| 266 } | |
| 267 } | |
| 268 // Always add phone number at the end. | |
| 269 editor_fields_.emplace_back( | |
| 270 autofill::PHONE_HOME_NUMBER, | |
| 271 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE), | |
| 272 EditorField::LengthHint::HINT_LONG, /*required=*/false, | |
| 273 EditorField::ControlType::TEXTFIELD); | |
| 274 } | |
| 275 | |
| 276 void ShippingAddressEditorViewController::OnCountryChanged( | |
| 277 views::Combobox* combobox) { | |
| 278 // TODO(crbug.com/703764): save the current state so we can map it to the new | |
| 279 // country fields as best we can. | |
| 280 | |
| 281 // Make sure to unfocus the combobox so we can remove it when we update the | |
| 282 // view. | |
| 283 combobox->SetFocusBehavior(views::View::FocusBehavior::NEVER); | |
| 284 UpdateEditorFields(); | |
| 285 // Let the focus change complete before updating the editor. | |
| 286 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 287 FROM_HERE, | |
| 288 base::Bind(&ShippingAddressEditorViewController::UpdateEditorView, | |
| 289 base::Unretained(this))); | |
| 290 } | |
| 291 | |
| 292 ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 293 ShippingAddressValidationDelegate( | |
| 294 ShippingAddressEditorViewController* parent, | |
| 295 const EditorField& field) | |
| 296 : field_(field) /* TODO(mad): Bring back when needed:, parent_(parent) */ {} | |
| 297 | |
| 298 ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 299 ~ShippingAddressValidationDelegate() {} | |
| 300 | |
| 301 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 302 ValidateTextfield(views::Textfield* textfield) { | |
| 303 return ValidateValue(textfield->text()); | |
| 304 } | |
| 305 | |
| 306 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 307 ValidateCombobox(views::Combobox* combobox) { | |
| 308 return ValidateValue(combobox->GetTextForRow(combobox->selected_index())); | |
| 309 } | |
| 310 | |
| 311 bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate:: | |
| 312 ValidateValue(const base::string16& value) { | |
| 313 // TODO(crbug.com/703754) add phone validation. | |
| 314 // TODO(crbug.com/703756): Display "required" error if applicable. | |
| 315 return !field_.required || !value.empty(); | |
| 316 } | |
| 317 | |
| 318 } // namespace payments | |
| OLD | NEW |