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

Unified Diff: chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc

Issue 2709093006: Adding new shipping address editor view to payment flow. (Closed)
Patch Set: Working version Created 3 years, 9 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..885381c0898a0df63381dabf1e5a781ae28e2e93
--- /dev/null
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
@@ -0,0 +1,329 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/autofill/validation_rules_storage_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/validating_combobox.h"
+#include "chrome/browser/ui/views/payments/validating_textfield.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/browser/autofill_address_util.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/country_combobox_model.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/region_combobox_model.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#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.
+#include "content/public/browser/web_contents.h"
+#include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
+#include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
+#include "third_party/libaddressinput/messages.h"
+// TODO(mad): Bring back when we solved the double base includes issues.
+// #include "third_party/libphonenumber/phonenumber_api.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/textfield/textfield.h"
+
+// TODO(mad): Bring back when we can include libphonenumber/phonenumber_api.h.
+// using ::i18n::phonenumbers::PhoneNumber;
+// using ::i18n::phonenumbers::PhoneNumberUtil;
+
+namespace payments {
+
+namespace {
+
+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.
+ "https://chromium-i18n.appspot.com/ssl-aggregate-address/";
+
+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.
+ if (type == autofill::kFullNameField)
+ return autofill::NAME_FULL;
+ if (type == autofill::kCompanyNameField)
+ return autofill::COMPANY_NAME;
+ if (type == autofill::kAddressLineField)
+ return autofill::ADDRESS_HOME_STREET_ADDRESS;
+ if (type == autofill::kDependentLocalityField)
+ return autofill::ADDRESS_HOME_DEPENDENT_LOCALITY;
+ if (type == autofill::kCityField)
+ return autofill::ADDRESS_HOME_CITY;
+ if (type == autofill::kStateField)
+ return autofill::ADDRESS_HOME_STATE;
+ if (type == autofill::kPostalCodeField)
+ return autofill::ADDRESS_HOME_ZIP;
+ if (type == autofill::kSortingCodeField)
+ return autofill::ADDRESS_HOME_SORTING_CODE;
+ if (type == autofill::kCountryField)
+ return autofill::ADDRESS_HOME_COUNTRY;
+ NOTREACHED();
+ return autofill::UNKNOWN_TYPE;
+}
+
+} // namespace
+
+ShippingAddressEditorViewController::ShippingAddressEditorViewController(
+ PaymentRequestSpec* spec,
+ PaymentRequestState* state,
+ PaymentRequestDialogView* dialog)
+ : EditorViewController(spec, state, dialog) {
+ UpdateEditorFields();
+}
+
+ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {}
+
+std::unique_ptr<views::View>
+ShippingAddressEditorViewController::CreateHeaderView() {
+ return base::MakeUnique<views::View>();
+}
+
+int ShippingAddressEditorViewController::GetViewHeaderTitleId() const {
+ return IDS_PAYMENT_REQUEST_ADDRESS_EDITOR_ADD_TITLE;
+}
+
+std::vector<EditorField>
+ShippingAddressEditorViewController::GetFieldDefinitions() {
+ return editor_fields_;
+}
+
+bool ShippingAddressEditorViewController::ValidateModelAndSave() {
+ autofill::AutofillProfile profile;
+ profile.set_origin(autofill::kSettingsOrigin);
+ for (const auto& field : text_fields()) {
+ // Force a blur in case the value was left untouched.
+ field.first->OnBlur();
+ // ValidatingTextfield* is the key, EditorField is the value.
+ if (field.first->invalid())
+ return false;
+
+ profile.SetRawInfo(field.second.type, field.first->text());
+ }
+ for (const auto& field : comboboxes()) {
+ // ValidatingCombobox* is the key, EditorField is the value.
+ ValidatingCombobox* combobox = field.first;
+ if (combobox->invalid())
+ return false;
+
+ if (combobox->id() == autofill::ADDRESS_HOME_COUNTRY) {
+ profile.SetRawInfo(
+ field.second.type,
+ base::UTF8ToUTF16(country_codes_[combobox->selected_index()]));
+ } else {
+ profile.SetRawInfo(field.second.type,
+ combobox->GetTextForRow(combobox->selected_index()));
+ }
+ }
+
+ // Add the profile (will not add a duplicate).
+ state()->GetPersonalDataManager()->AddProfile(profile);
+
+ return true;
+}
+
+std::unique_ptr<ValidationDelegate>
+ShippingAddressEditorViewController::CreateValidationDelegate(
+ const EditorField& field) {
+ return base::MakeUnique<
+ ShippingAddressEditorViewController::ShippingAddressValidationDelegate>(
+ this, field);
+}
+
+std::unique_ptr<ui::ComboboxModel>
+ShippingAddressEditorViewController::GetComboboxModelForType(
+ const autofill::ServerFieldType& type) {
+ switch (type) {
+ case autofill::ADDRESS_HOME_COUNTRY: {
+ autofill::CountryComboboxModel* model =
+ new autofill::CountryComboboxModel();
+ model->SetCountries(*state()->GetPersonalDataManager(),
+ base::Callback<bool(const std::string&)>(),
+ state()->GetPersonalDataManager()->app_locale());
+ country_codes_.clear();
+ for (size_t i = 0; i < model->countries().size(); ++i) {
+ if (model->countries()[i].get())
+ country_codes_.push_back(model->countries()[i]->country_code());
+ else
+ country_codes_.push_back(""); // Separator.
+ }
+ return std::unique_ptr<ui::ComboboxModel>(model);
+ }
+ case autofill::ADDRESS_HOME_STATE: {
+ return std::unique_ptr<
+ ui::ComboboxModel>(new autofill::RegionComboboxModel(
+ base::WrapUnique(new autofill::ChromeMetadataSource(
+ kAddressValidationDataURL,
+ state()->GetPersonalDataManager()->GetURLRequestContextGetter())),
+ autofill::ValidationRulesStorageFactory::CreateStorage(),
+ state()->GetPersonalDataManager()->app_locale(),
+ country_codes_[chosen_country_index_]));
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+ return std::unique_ptr<ui::ComboboxModel>();
+}
+
+void ShippingAddressEditorViewController::OnPerformAction(
+ views::Combobox* sender) {
+ if (sender->id() != autofill::ADDRESS_HOME_COUNTRY)
+ return;
+ if (chosen_country_index_ != sender->selected_index()) {
+ chosen_country_index_ = sender->selected_index();
+ OnCountryChanged(sender);
+ }
+}
+
+void ShippingAddressEditorViewController::UpdateEditorView() {
+ EditorViewController::UpdateEditorView();
+ if (chosen_country_index_ > 0) {
+ views::Combobox* country_combo_box = static_cast<views::Combobox*>(
+ dialog()->GetViewByID(autofill::ADDRESS_HOME_COUNTRY));
+ DCHECK(country_combo_box);
+ country_combo_box->SetSelectedIndex(chosen_country_index_);
+ }
+}
+
+void ShippingAddressEditorViewController::UpdateEditorFields() {
+ editor_fields_.clear();
+ 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.
+ std::string unused;
Mathieu 2017/03/21 15:55:25 same
MAD 2017/03/21 19:28:33 Done.
+ std::string chosen_country_code;
+ if (chosen_country_index_ < country_codes_.size())
+ chosen_country_code = country_codes_[chosen_country_index_];
+
+ autofill::GetAddressComponents(
+ chosen_country_code, state()->GetPersonalDataManager()->app_locale(),
Mathieu 2017/03/21 15:55:25 state()->GetApplicationLocale()
MAD 2017/03/21 19:28:33 Done.
+ components.get(), &unused);
+
+ 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... ;
+ const base::ListValue* line = nullptr;
+ if (!components->GetList(lineIndex, &line)) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK_NE(nullptr, line);
+ 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.
+ ++componentIndex) {
+ const base::DictionaryValue* component = nullptr;
+ if (!line->GetDictionary(componentIndex, &component)) {
+ NOTREACHED();
+ return;
+ }
+ std::string field_type;
+ if (!component->GetString(autofill::kFieldTypeKey, &field_type)) {
+ NOTREACHED();
+ return;
+ }
+ std::string field_name;
+ if (!component->GetString(autofill::kFieldNameKey, &field_name)) {
+ NOTREACHED();
+ return;
+ }
+ std::string field_length;
+ if (!component->GetString(autofill::kFieldLengthKey, &field_length)) {
+ NOTREACHED();
+ return;
+ }
+ EditorField::LengthHint length_hint = EditorField::LengthHint::HINT_LONG;
+ if (field_length == autofill::kShortField)
+ length_hint = EditorField::LengthHint::HINT_SHORT;
+ else
+ DCHECK_EQ(autofill::kLongField, field_length);
+ autofill::ServerFieldType server_field_type =
+ GetFieldTypeFromString(field_type);
+ EditorField::ControlType control_type =
+ EditorField::ControlType::TEXTFIELD;
+ if (server_field_type == autofill::ADDRESS_HOME_COUNTRY ||
+ server_field_type == autofill::ADDRESS_HOME_STATE) {
+ control_type = EditorField::ControlType::COMBOBOX;
+ }
+ editor_fields_.push_back(std::move<EditorField>(EditorField(
+ server_field_type, base::UTF8ToUTF16(field_name), length_hint,
+ 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.
+ control_type)));
+ // Insert the Country combo box right after NAME_FULL.
+ if (server_field_type == autofill::NAME_FULL) {
+ editor_fields_.push_back(std::move<EditorField>(
+ EditorField(autofill::ADDRESS_HOME_COUNTRY,
+ l10n_util::GetStringUTF16(
+ IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL),
+ EditorField::LengthHint::HINT_LONG, true,
Mathieu 2017/03/21 15:55:25 /*required=*/true
MAD 2017/03/21 19:28:33 Done.
+ EditorField::ControlType::COMBOBOX)));
+ }
+ }
+ }
+ // Always add phone number at the end.
+ editor_fields_.push_back(std::move<EditorField>(
+ EditorField(autofill::PHONE_HOME_NUMBER,
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE),
+ EditorField::LengthHint::HINT_SHORT, false, // required.
Mathieu 2017/03/21 15:55:25 nit: /*required=*/false
MAD 2017/03/21 19:28:33 Done.
+ EditorField::ControlType::TEXTFIELD)));
+}
+
+void ShippingAddressEditorViewController::OnCountryChanged(
+ views::Combobox* combobox) {
+ // 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.
+ // view.
+ combobox->SetFocusBehavior(views::View::FocusBehavior::NEVER);
+ UpdateEditorFields();
+ // Let the focus change complete before updating the editor.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&ShippingAddressEditorViewController::UpdateEditorView,
+ base::Unretained(this)));
+}
+
+ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
+ ShippingAddressValidationDelegate(
+ ShippingAddressEditorViewController* parent,
+ const EditorField& field)
+ : field_(field), parent_(parent) {}
+
+ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
+ ~ShippingAddressValidationDelegate() {}
+
+bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
+ ValidateTextfield(views::Textfield* textfield) {
+ return ValidateValue(textfield->text());
+}
+
+bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
+ ValidateCombobox(views::Combobox* combobox) {
+ return ValidateValue(combobox->GetTextForRow(combobox->selected_index()));
+}
+
+bool ShippingAddressEditorViewController::ShippingAddressValidationDelegate::
+ ValidateValue(const base::string16& value) {
+ /*
+ 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.
+ if (field_.type == autofill::PHONE_HOME_NUMBER) {
+ PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
+ PhoneNumber number_object;
+ 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.
+ base::UTF16ToUTF8(value),
+ parent_->country_codes_[parent_->chosen_country_index_],
+ &number_object) == PhoneNumberUtil::NO_PARSING_ERROR;
+
+ }*/
+ // 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.
+ return !field_.required || !value.empty();
+}
+
+} // namespace payments

Powered by Google App Engine
This is Rietveld 408576698