Commit f4de0ad8 authored by dbeam@chromium.org's avatar dbeam@chromium.org

Add country combobox to change country and rebuild address inputs.

R=estade@chromium.org
BUG=331544

Review URL: https://codereview.chromium.org/124533003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245434 0039d316-1c4b-4281-b951-d872f2087c98
parent 3e27652e
......@@ -264,6 +264,10 @@ AutofillMetrics::DialogUiEvent DialogSectionToUiSelectionChangedEvent(
}
base::string16 GetHardcodedValueForType(ServerFieldType type) {
// TODO(dbeam): remove this entire function when i18n inputs are the default.
if (IsI18nInputEnabled())
return base::string16();
if (AutofillType(type).GetStorableType() == ADDRESS_HOME_COUNTRY) {
AutofillCountry country("US", g_browser_process->GetApplicationLocale());
return country.name();
......
......@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "chrome/browser/ui/autofill/account_chooser_model.h"
......@@ -36,6 +37,7 @@
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/ssl_status.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/models/combobox_model_observer.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/animation/animation_delegate.h"
......@@ -76,7 +78,8 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
public wallet::WalletSigninHelperDelegate,
public PersonalDataManagerObserver,
public AccountChooserModelDelegate,
public gfx::AnimationDelegate {
public gfx::AnimationDelegate,
public ui::ComboboxModelObserver {
public:
virtual ~AutofillDialogControllerImpl();
......@@ -221,6 +224,9 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
// ui::ComboboxModelObserver implementation.
virtual void OnComboboxModelChanged(ui::ComboboxModel* model) OVERRIDE;
protected:
enum DialogSignedInState {
NOT_CHECKED,
......@@ -269,6 +275,10 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
// Opens the given URL in a new foreground tab.
virtual void OpenTabWithUrl(const GURL& url);
// The active billing section for the current state of the dialog (e.g. when
// paying for wallet, the combined credit card + billing address section).
DialogSection ActiveBillingSection() const;
// Whether |section| was sent into edit mode based on existing data. This
// happens when a user clicks "Edit" or a suggestion is invalid.
virtual bool IsEditingExistingData(DialogSection section) const;
......@@ -422,6 +432,9 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
DialogSection SectionForSuggestionsMenuModel(
const SuggestionsMenuModel& model);
// Gets the CountryComboboxModel for |section|.
CountryComboboxModel* CountryComboboxModelForSection(DialogSection section);
// Suggested text and icons for sections. Suggestion text is used to show an
// abridged overview of the currently used suggestion. Extra text is used when
// part of a section is suggested but part must be manually input (e.g. during
......@@ -449,6 +462,9 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
// Like RequestedFieldsForSection, but returns a pointer.
DetailInputs* MutableRequestedFieldsForSection(DialogSection section);
// Returns the country code (e.g. "US") for |section|.
std::string CountryCodeForSection(DialogSection section);
// Hides |popup_controller_|'s popup view, if it exists.
void HidePopup();
......@@ -649,8 +665,9 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
MonthComboboxModel cc_exp_month_combobox_model_;
YearComboboxModel cc_exp_year_combobox_model_;
// Model for the country input.
CountryComboboxModel country_combobox_model_;
// Models for country input.
CountryComboboxModel billing_country_combobox_model_;
CountryComboboxModel shipping_country_combobox_model_;
// Models for the suggestion views.
SuggestionsMenuModel suggested_cc_;
......@@ -758,6 +775,8 @@ class AutofillDialogControllerImpl : public AutofillDialogViewDelegate,
// A username string we display in the card scrambling/generated overlay.
base::string16 submitted_cardholder_name_;
ScopedObserver<ui::ComboboxModel, AutofillDialogControllerImpl> observer_;
DISALLOW_COPY_AND_ASSIGN(AutofillDialogControllerImpl);
};
......
......@@ -21,6 +21,7 @@
#include "chrome/browser/ui/autofill/mock_new_credit_card_bubble_controller.h"
#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h"
#include "chrome/browser/webdata/web_data_service_factory.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
......@@ -170,6 +171,7 @@ class TestAutofillDialogView : public AutofillDialogView {
}
virtual void UpdateSection(DialogSection section) OVERRIDE {
section_updates_[section]++;
EXPECT_GE(updates_started_, 1);
}
......@@ -215,8 +217,17 @@ class TestAutofillDialogView : public AutofillDialogView {
save_details_locally_checked_ = checked;
}
void ClearSectionUpdates() {
section_updates_.clear();
}
std::map<DialogSection, size_t> section_updates() const {
return section_updates_;
}
private:
std::map<DialogSection, FieldValueMap> outputs_;
std::map<DialogSection, size_t> section_updates_;
int updates_started_;
bool save_details_locally_checked_;
......@@ -2923,4 +2934,49 @@ TEST_F(AutofillDialogControllerTest, IconReservedForCreditCardField) {
}
}
TEST_F(AutofillDialogControllerTest, CountryChangeUpdatesSection) {
CommandLine* command_line = CommandLine::ForCurrentProcess();
command_line->AppendSwitch(::switches::kEnableAutofillAddressI18n);
Reset();
TestAutofillDialogView* view = controller()->GetView();
view->ClearSectionUpdates();
controller()->UserEditedOrActivatedInput(SECTION_SHIPPING,
ADDRESS_HOME_COUNTRY,
gfx::NativeView(),
gfx::Rect(),
ASCIIToUTF16("China"),
true);
std::map<DialogSection, size_t> updates = view->section_updates();
EXPECT_EQ(1U, updates[SECTION_SHIPPING]);
EXPECT_EQ(1U, updates.size());
view->ClearSectionUpdates();
controller()->UserEditedOrActivatedInput(SECTION_CC_BILLING,
ADDRESS_BILLING_COUNTRY,
gfx::NativeView(),
gfx::Rect(),
ASCIIToUTF16("France"),
true);
updates = view->section_updates();
EXPECT_EQ(1U, updates[SECTION_CC_BILLING]);
EXPECT_EQ(1U, updates.size());
SwitchToAutofill();
view->ClearSectionUpdates();
controller()->UserEditedOrActivatedInput(SECTION_BILLING,
ADDRESS_BILLING_COUNTRY,
gfx::NativeView(),
gfx::Rect(),
ASCIIToUTF16("Italy"),
true);
updates = view->section_updates();
EXPECT_EQ(1U, updates[SECTION_BILLING]);
EXPECT_EQ(1U, updates.size());
}
} // namespace autofill
......@@ -98,8 +98,7 @@ void BuildAddressInputs(common::AddressType address_type,
billing ? ADDRESS_BILLING_COUNTRY : ADDRESS_HOME_COUNTRY;
base::string16 placeholder_text =
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_COUNTRY);
// TODO(dbeam): unhide so users can switch countries. http://crbug.com/331544
DetailInput input = { DetailInput::NONE, server_type, placeholder_text };
DetailInput input = { DetailInput::LONG, server_type, placeholder_text };
inputs->push_back(input);
}
......
......@@ -154,9 +154,9 @@ class AutofillDialogViewDelegate {
virtual ValidityMessages InputsAreValid(DialogSection section,
const FieldValueMap& inputs) = 0;
// Called when the user changes the contents of a text field or activates it
// (by focusing and then clicking it). |was_edit| is true when the function
// was called in response to the user editing the text field.
// Called when the user edits or activates a textfield or combobox.
// |was_edit| is true when the function was called in response to the user
// editing the input.
virtual void UserEditedOrActivatedInput(DialogSection section,
ServerFieldType type,
gfx::NativeView parent_view,
......
......@@ -4,17 +4,20 @@
#include "chrome/browser/ui/autofill/country_combobox_model.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "components/autofill/core/browser/autofill_country.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "ui/base/l10n/l10n_util_collator.h"
#include "ui/base/models/combobox_model_observer.h"
namespace autofill {
CountryComboboxModel::CountryComboboxModel(const PersonalDataManager& manager) {
CountryComboboxModel::CountryComboboxModel(const PersonalDataManager& manager)
: default_index_(0) {
// Insert the default country at the top as well as in the ordered list.
std::string app_locale = g_browser_process->GetApplicationLocale();
const std::string& app_locale = g_browser_process->GetApplicationLocale();
std::string default_country_code =
manager.GetDefaultCountryCodeForNewAddress();
DCHECK(!default_country_code.empty());
......@@ -61,4 +64,50 @@ bool CountryComboboxModel::IsItemSeparatorAt(int index) {
return !countries_[index];
}
int CountryComboboxModel::GetDefaultIndex() const {
return default_index_;
}
void CountryComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
observers_.AddObserver(observer);
}
void CountryComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
observers_.RemoveObserver(observer);
}
void CountryComboboxModel::ResetDefault() {
SetDefaultIndex(0);
}
void CountryComboboxModel::SetDefaultCountry(const std::string& country_code) {
DCHECK_EQ(2U, country_code.length());
for (size_t i = 0; i < countries_.size(); ++i) {
if (countries_[i] && countries_[i]->country_code() == country_code) {
SetDefaultIndex(i);
return;
}
}
NOTREACHED();
}
std::string CountryComboboxModel::GetDefaultCountryCode() const {
return countries_[default_index_]->country_code();
}
void CountryComboboxModel::SetDefaultIndex(int index) {
DCHECK_GE(index, 0);
DCHECK_LT(index, static_cast<int>(countries_.size()));
DCHECK(!IsItemSeparatorAt(index));
if (index == default_index_)
return;
default_index_ = index;
FOR_EACH_OBSERVER(ui::ComboboxModelObserver, observers_,
OnComboboxModelChanged(this));
}
} // namespace autofill
......@@ -5,10 +5,12 @@
#ifndef CHROME_BROWSER_UI_AUTOFILL_COUNTRY_COMBOBOX_MODEL_H_
#define CHROME_BROWSER_UI_AUTOFILL_COUNTRY_COMBOBOX_MODEL_H_
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/scoped_vector.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
#include "ui/base/models/combobox_model.h"
......@@ -23,20 +25,41 @@ class CountryComboboxModel : public ui::ComboboxModel {
explicit CountryComboboxModel(const PersonalDataManager& manager);
virtual ~CountryComboboxModel();
// ui::Combobox implementation:
// ui::ComboboxModel implementation:
virtual int GetItemCount() const OVERRIDE;
virtual base::string16 GetItemAt(int index) OVERRIDE;
virtual bool IsItemSeparatorAt(int index) OVERRIDE;
virtual int GetDefaultIndex() const OVERRIDE;
virtual void AddObserver(ui::ComboboxModelObserver* observer) OVERRIDE;
virtual void RemoveObserver(ui::ComboboxModelObserver* observer) OVERRIDE;
const std::vector<AutofillCountry*>& countries() const {
return countries_.get();
}
// Clears the default country.
void ResetDefault();
// Changes |country_code| to the default country.
void SetDefaultCountry(const std::string& country_code);
// Returns the default country code for this model.
std::string GetDefaultCountryCode() const;
private:
// Changes |default_index_| to |index| after checking it's sane. Notifies
// observers if |default_index_| changed.
void SetDefaultIndex(int index);
// The countries to show in the model, including NULL for entries that are
// not countries (the separator entry).
ScopedVector<AutofillCountry> countries_;
// The index of the default country.
int default_index_;
ObserverList<ui::ComboboxModelObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(CountryComboboxModel);
};
......
// Copyright 2014 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/autofill/country_combobox_model.h"
#include "components/autofill/core/browser/autofill_country.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/combobox_model_observer.h"
namespace autofill {
namespace {
const char kTestCountry[] = "AQ";
class MockObserver : public ui::ComboboxModelObserver {
public:
MOCK_METHOD1(OnComboboxModelChanged, void(ui::ComboboxModel* model));
};
} // namespace
TEST(CountryComboboxModel, SetAndResetDefaultCountry) {
TestPersonalDataManager manager;
CountryComboboxModel model(manager);
MockObserver observer;
model.AddObserver(&observer);
EXPECT_CALL(observer, OnComboboxModelChanged(&model)).Times(2);
std::string default_country = model.GetDefaultCountryCode();
ASSERT_NE(default_country, kTestCountry);
model.SetDefaultCountry(kTestCountry);
EXPECT_EQ(kTestCountry, model.GetDefaultCountryCode());
model.ResetDefault();
EXPECT_EQ(default_country, model.GetDefaultCountryCode());
}
TEST(CountryComboboxModel, RespectsManagerDefaultCountry) {
TestPersonalDataManager manager;
manager.set_timezone_country_code(kTestCountry);
CountryComboboxModel model(manager);
EXPECT_EQ(kTestCountry, model.GetDefaultCountryCode());
}
} // namespace autofill
......@@ -71,8 +71,10 @@
}
- (void)didSelectItem:(id)sender {
if (inputDelegate_)
if (inputDelegate_) {
[inputDelegate_ didChange:self];
[inputDelegate_ didEndEditing:self];
}
}
@end
......
......@@ -463,9 +463,9 @@ class AutofillDialogViews : public AutofillDialogView,
SectionContainer* container;
// The view that allows manual input.
views::View* manual_input;
// The textfields in |manual_input|, tracked by their DetailInput.
// The textfields in |manual_input|, tracked by their ServerFieldType.
TextfieldMap textfields;
// The comboboxes in |manual_input|, tracked by their DetailInput.
// The comboboxes in |manual_input|, tracked by their ServerFieldType.
ComboboxMap comboboxes;
// The view that holds the text of the suggested data. This will be
// visible IFF |manual_input| is not visible.
......@@ -507,10 +507,8 @@ class AutofillDialogViews : public AutofillDialogView,
// a given section.
views::View* CreateInputsContainer(DialogSection section);
// Creates a grid of textfield views for the given section, and stores them
// in the appropriate DetailsGroup. The top level View in the hierarchy is
// returned.
views::View* InitInputsView(DialogSection section);
// Creates a grid of inputs for the given section.
void InitInputsView(DialogSection section);
// Changes the function of the whole dialog. Currently this can show a loading
// shield, an embedded sign in web view, or the more typical detail input mode
......@@ -533,6 +531,9 @@ class AutofillDialogViews : public AutofillDialogView,
// Returns NULL if no DetailsGroup was found.
DetailsGroup* GroupForView(views::View* view);
// Erases all views in |group| from |validity_map_|.
void EraseInvalidViewsInGroup(const DetailsGroup* group);
// Explicitly focuses the initially focusable view.
void FocusInitialView();
......@@ -563,11 +564,13 @@ class AutofillDialogViews : public AutofillDialogView,
// ones and returns true if all were valid.
bool ValidateForm();
// When an input textfield is edited (its contents change) or activated
// (clicked while focused), this function will inform the delegate that it's
// time to show a suggestion popup and possibly reset the validity state of
// the input.
void TextfieldEditedOrActivated(views::Textfield* textfield, bool was_edit);
// When an input is edited (its contents change) or activated (clicked while
// focused), this function will inform the delegate to take the appropriate
// action (textfields may show a suggestion popup, comboboxes may rebuild the
// section inputs). May also reset the validity state of the input.
void InputEditedOrActivated(ServerFieldType type,
const gfx::Rect& bounds,
bool was_edit);
// Updates the views in the button strip.
void UpdateButtonStripExtraView();
......@@ -577,11 +580,17 @@ class AutofillDialogViews : public AutofillDialogView,
void DoContentsPreferredSizeChanged();
// Gets the textfield view that is shown for the given |type| or NULL.
views::Textfield* TextfieldForType(ServerFieldType type);
DecoratedTextfield* TextfieldForType(ServerFieldType type);
// Returns the associated ServerFieldType for |textfield|.
ServerFieldType TypeForTextfield(const views::Textfield* textfield);
// Gets the combobox view that is shown for the given |type|, or NULL.
views::Combobox* ComboboxForType(ServerFieldType type);
// Returns the associated ServerFieldType for |combobox|.
ServerFieldType TypeForCombobox(const views::Combobox* combobox) const;
// Called when the details container changes in size or position.
void DetailsContainerBoundsChanged();
......
......@@ -1432,6 +1432,7 @@
'browser/ui/autofill/autofill_dialog_models_unittest.cc',
'browser/ui/autofill/autofill_dialog_types_unittest.cc',
'browser/ui/autofill/autofill_popup_controller_unittest.cc',
'browser/ui/autofill/country_combobox_model_unittest.cc',
'browser/ui/autofill/data_model_wrapper_unittest.cc',
'browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc',
'browser/ui/autofill/mock_autofill_dialog_view_delegate.cc',
......@@ -2553,6 +2554,7 @@
'browser/tab_contents/language_state_unittest.cc',
'browser/tab_contents/render_view_context_menu_unittest.cc',
'browser/ui/autofill/autofill_dialog_controller_unittest.cc',
'browser/ui/autofill/country_combobox_model_unittest.cc',
'browser/ui/browser_instant_controller_unittest.cc',
'browser/ui/bookmarks/bookmark_bubble_sign_in_delegate_unittest.cc',
'browser/ui/bookmarks/bookmark_context_menu_controller_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment