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 |