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" | |
Mathieu
2017/03/21 15:55:24
nit: payment_request_state.h
MAD
2017/03/21 19:28:33
Done.
| |
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 = | |
Mathieu
2017/03/21 15:55:25
See how the URL is used as a compile-time constant
MAD
2017/03/21 19:28:33
Done.
| |
54 "https://chromium-i18n.appspot.com/ssl-aggregate-address/"; | |
55 | |
56 autofill::ServerFieldType GetFieldTypeFromString(const std::string& type) { | |
Mathieu
2017/03/21 15:55:25
Let's put a small comment to define input and outp
MAD
2017/03/21 19:28:33
Done.
| |
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); | |
Mathieu
2017/03/21 15:55:25
nit: would bring closer to line 211
MAD
2017/03/21 19:28:33
Done.
| |
206 std::string unused; | |
Mathieu
2017/03/21 15:55:25
same
MAD
2017/03/21 19:28:33
Done.
| |
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(), | |
Mathieu
2017/03/21 15:55:25
state()->GetApplicationLocale()
MAD
2017/03/21 19:28:33
Done.
| |
213 components.get(), &unused); | |
214 | |
215 for (size_t lineIndex = 0; lineIndex < components->GetSize(); ++lineIndex) { | |
Mathieu
2017/03/21 15:55:25
nit: line_index
MAD
2017/03/21 19:28:33
Héhé... I've done too much JavaScript in 2016... ;
| |
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(); | |
Mathieu
2017/03/21 15:55:25
same nit
MAD
2017/03/21 19:28:33
Done.
| |
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(std::move<EditorField>(EditorField( | |
258 server_field_type, base::UTF8ToUTF16(field_name), length_hint, | |
259 server_field_type != autofill::COMPANY_NAME, // required. | |
Mathieu
2017/03/21 15:55:25
nit: In other places we use /*required=*/server_fi
MAD
2017/03/21 19:28:34
Done.
| |
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(std::move<EditorField>( | |
264 EditorField(autofill::ADDRESS_HOME_COUNTRY, | |
265 l10n_util::GetStringUTF16( | |
266 IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL), | |
267 EditorField::LengthHint::HINT_LONG, true, | |
Mathieu
2017/03/21 15:55:25
/*required=*/true
MAD
2017/03/21 19:28:33
Done.
| |
268 EditorField::ControlType::COMBOBOX))); | |
269 } | |
270 } | |
271 } | |
272 // Always add phone number at the end. | |
273 editor_fields_.push_back(std::move<EditorField>( | |
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 15:55:25
nit: /*required=*/false
MAD
2017/03/21 19:28:33
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 | |
Mathieu
2017/03/21 15:55:25
nit: combobox
MAD
2017/03/21 19:28:33
Done.
| |
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), 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. | |
Mathieu
2017/03/21 15:55:25
Let's create and put a bug number for this: TODO(c
MAD
2017/03/21 19:28:33
Done.
| |
316 if (field_.type == autofill::PHONE_HOME_NUMBER) { | |
317 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); | |
318 PhoneNumber number_object; | |
319 return phone_util->Parse( | |
Mathieu
2017/03/21 15:55:25
note for later: after parsing, there is also "IsVa
MAD
2017/03/21 19:28:33
Done.
| |
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. | |
Mathieu
2017/03/21 15:55:25
Let's create a TODO(crbug.com/XXXXXX) for this sin
MAD
2017/03/21 19:28:33
Done.
| |
326 return !field_.required || !value.empty(); | |
327 } | |
328 | |
329 } // namespace payments | |
OLD | NEW |