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

Making credit card number un-editable in Wallet mode.

Also adds a way of generically making any textfield or combobox disabled if necessary.

R=estade@chromium.org
BUG=180147

Review URL: https://chromiumcodereview.appspot.com/15979002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202847 0039d316-1c4b-4281-b951-d872f2087c98
parent 74b18cf2
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "components/autofill/browser/autofill_common_test.h" #include "components/autofill/browser/autofill_common_test.h"
#include "components/autofill/browser/autofill_metrics.h" #include "components/autofill/browser/autofill_metrics.h"
#include "components/autofill/browser/test_personal_data_manager.h" #include "components/autofill/browser/test_personal_data_manager.h"
#include "components/autofill/browser/wallet/wallet_test_util.h"
#include "components/autofill/common/form_data.h" #include "components/autofill/common/form_data.h"
#include "components/autofill/common/form_field_data.h" #include "components/autofill/common/form_field_data.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
...@@ -87,9 +88,7 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl { ...@@ -87,9 +88,7 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl {
dialog_type, dialog_type,
base::Bind(&MockCallback)), base::Bind(&MockCallback)),
metric_logger_(metric_logger), metric_logger_(metric_logger),
message_loop_runner_(runner) { message_loop_runner_(runner) {}
DisableWallet();
}
virtual ~TestAutofillDialogController() {} virtual ~TestAutofillDialogController() {}
...@@ -126,6 +125,8 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl { ...@@ -126,6 +125,8 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl {
return &test_manager_; return &test_manager_;
} }
using AutofillDialogControllerImpl::DisableWallet;
protected: protected:
virtual PersonalDataManager* GetManager() OVERRIDE { virtual PersonalDataManager* GetManager() OVERRIDE {
return &test_manager_; return &test_manager_;
...@@ -266,6 +267,7 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) { ...@@ -266,6 +267,7 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocheckoutError) {
IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) { IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillInputFromAutofill) {
InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
controller()->DisableWallet();
AutofillProfile full_profile(test::GetFullProfile()); AutofillProfile full_profile(test::GetFullProfile());
controller()->GetTestingManager()->AddTestingProfile(&full_profile); controller()->GetTestingManager()->AddTestingProfile(&full_profile);
...@@ -343,6 +345,7 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, ...@@ -343,6 +345,7 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
// expected when Autofill is used to fill text inputs. // expected when Autofill is used to fill text inputs.
IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) { IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) {
InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE); InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
controller()->DisableWallet();
CreditCard card1; CreditCard card1;
test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018"); test::SetCreditCardInfo(&card1, "JJ Smith", "4111111111111111", "12", "2018");
...@@ -424,6 +427,44 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) { ...@@ -424,6 +427,44 @@ IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, FillComboboxFromAutofill) {
} }
} }
} }
// Tests that credit card number is disabled while editing a Wallet instrument.
IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, WalletCreditCardDisabled) {
InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
controller()->OnUserNameFetchSuccess("user@example.com");
scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems();
wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
controller()->OnDidGetWalletItems(wallet_items.Pass());
// Click "Edit" in the billing section (while using Wallet).
controller()->EditClickedForSection(SECTION_CC_BILLING);
const DetailInputs& edit_inputs =
controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
size_t i;
for (i = 0; i < edit_inputs.size(); ++i) {
if (edit_inputs[i].type == CREDIT_CARD_NUMBER) {
EXPECT_FALSE(edit_inputs[i].editable);
break;
}
}
ASSERT_LT(i, edit_inputs.size());
// Select "Add new billing info..." while using Wallet.
ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC_BILLING);
model->ActivatedAt(model->GetItemCount() - 2);
const DetailInputs& add_inputs =
controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
for (i = 0; i < add_inputs.size(); ++i) {
if (add_inputs[i].type == CREDIT_CARD_NUMBER) {
EXPECT_TRUE(add_inputs[i].editable);
break;
}
}
ASSERT_LT(i, add_inputs.size());
}
#endif // defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_VIEWS)
} // namespace autofill } // namespace autofill
...@@ -778,23 +778,27 @@ void AutofillDialogControllerImpl::EnsureLegalDocumentsText() { ...@@ -778,23 +778,27 @@ void AutofillDialogControllerImpl::EnsureLegalDocumentsText() {
void AutofillDialogControllerImpl::PrepareDetailInputsForSection( void AutofillDialogControllerImpl::PrepareDetailInputsForSection(
DialogSection section) { DialogSection section) {
// Reset all previously entered data and stop editing |section|.
DetailInputs* inputs = MutableRequestedFieldsForSection(section);
for (size_t i = 0; i < inputs->size(); ++i) {
(*inputs)[i].initial_value.clear();
}
section_editing_state_[section] = false; section_editing_state_[section] = false;
scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
// If the chosen item in |model| yields an empty suggestion text, it is // If the chosen item in |model| yields an empty suggestion text, it is
// invalid. In this case, show the editing UI with invalid fields highlighted. // invalid. In this case, show the editing UI with invalid fields highlighted.
SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) && if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) &&
SuggestionTextForSection(section).empty()) { SuggestionTextForSection(section).empty()) {
scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
wrapper->FillInputs(MutableRequestedFieldsForSection(section));
section_editing_state_[section] = true; section_editing_state_[section] = true;
} }
// Reset all previously entered data and stop editing |section|.
DetailInputs* inputs = MutableRequestedFieldsForSection(section);
for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
it->initial_value.clear();
it->editable = InputIsEditable(*it, section);
}
if (wrapper && section_editing_state_[section])
wrapper->FillInputs(inputs);
if (view_) if (view_)
view_->UpdateSection(section); view_->UpdateSection(section);
} }
...@@ -1080,8 +1084,14 @@ bool AutofillDialogControllerImpl::EditEnabledForSection( ...@@ -1080,8 +1084,14 @@ bool AutofillDialogControllerImpl::EditEnabledForSection(
void AutofillDialogControllerImpl::EditClickedForSection( void AutofillDialogControllerImpl::EditClickedForSection(
DialogSection section) { DialogSection section) {
scoped_ptr<DataModelWrapper> model = CreateWrapper(section); scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
model->FillInputs(MutableRequestedFieldsForSection(section));
section_editing_state_[section] = true; section_editing_state_[section] = true;
DetailInputs* inputs = MutableRequestedFieldsForSection(section);
for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
it->editable = InputIsEditable(*it, section);
}
model->FillInputs(inputs);
view_->UpdateSection(section); view_->UpdateSection(section);
GetMetricLogger().LogDialogUiEvent( GetMetricLogger().LogDialogUiEvent(
...@@ -2361,6 +2371,20 @@ bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const { ...@@ -2361,6 +2371,20 @@ bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
return false; return false;
} }
bool AutofillDialogControllerImpl::InputIsEditable(
const DetailInput& input,
DialogSection section) const {
if (input.type != CREDIT_CARD_NUMBER || !IsPayingWithWallet())
return true;
std::map<DialogSection, bool>::const_iterator it =
section_editing_state_.find(section);
if (it != section_editing_state_.end() && it->second)
return false;
return true;
}
bool AutofillDialogControllerImpl::AllSectionsAreValid() const { bool AutofillDialogControllerImpl::AllSectionsAreValid() const {
for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) { for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
if (!SectionIsValid(static_cast<DialogSection>(section))) if (!SectionIsValid(static_cast<DialogSection>(section)))
......
...@@ -398,6 +398,9 @@ class AutofillDialogControllerImpl : public AutofillDialogController, ...@@ -398,6 +398,9 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// Whether the user has chosen to enter all new data in at least one section. // Whether the user has chosen to enter all new data in at least one section.
bool IsManuallyEditingAnySection() const; bool IsManuallyEditingAnySection() const;
// Whether a particular DetailInput in |section| should be edited or not.
bool InputIsEditable(const DetailInput& input, DialogSection section) const;
// Whether all of the input fields currently showing in the dialog have valid // Whether all of the input fields currently showing in the dialog have valid
// contents. // contents.
bool AllSectionsAreValid() const; bool AllSectionsAreValid() const;
......
...@@ -36,6 +36,9 @@ struct DetailInput { ...@@ -36,6 +36,9 @@ struct DetailInput {
// When non-empty, indicates the starting value for this input. This will be // When non-empty, indicates the starting value for this input. This will be
// used when the user is editing existing data. // used when the user is editing existing data.
string16 initial_value; string16 initial_value;
// Whether the input is able to be edited (e.g. text changed in textfields,
// index changed in comboboxes).
bool editable;
}; };
// Determines whether |input| and |field| match. // Determines whether |input| and |field| match.
......
...@@ -1506,6 +1506,7 @@ void AutofillDialogViews::UpdateSectionImpl( ...@@ -1506,6 +1506,7 @@ void AutofillDialogViews::UpdateSectionImpl(
if (text_mapping != group->textfields.end()) { if (text_mapping != group->textfields.end()) {
views::Textfield* textfield = text_mapping->second->textfield(); views::Textfield* textfield = text_mapping->second->textfield();
textfield->SetEnabled(input.editable);
if (textfield->text().empty() || clobber_inputs) { if (textfield->text().empty() || clobber_inputs) {
textfield->SetText(iter->initial_value); textfield->SetText(iter->initial_value);
textfield->SetIcon(controller_->IconForField( textfield->SetIcon(controller_->IconForField(
...@@ -1516,6 +1517,7 @@ void AutofillDialogViews::UpdateSectionImpl( ...@@ -1516,6 +1517,7 @@ void AutofillDialogViews::UpdateSectionImpl(
ComboboxMap::iterator combo_mapping = group->comboboxes.find(&input); ComboboxMap::iterator combo_mapping = group->comboboxes.find(&input);
if (combo_mapping != group->comboboxes.end()) { if (combo_mapping != group->comboboxes.end()) {
views::Combobox* combobox = combo_mapping->second; views::Combobox* combobox = combo_mapping->second;
combobox->SetEnabled(input.editable);
if (combobox->selected_index() == combobox->model()->GetDefaultIndex() || if (combobox->selected_index() == combobox->model()->GetDefaultIndex() ||
clobber_inputs) { clobber_inputs) {
for (int i = 0; i < combobox->model()->GetItemCount(); ++i) { for (int i = 0; i < combobox->model()->GetItemCount(); ++i) {
...@@ -1619,6 +1621,9 @@ bool AutofillDialogViews::ValidateGroup( ...@@ -1619,6 +1621,9 @@ bool AutofillDialogViews::ValidateGroup(
if (group.manual_input->visible()) { if (group.manual_input->visible()) {
for (TextfieldMap::const_iterator iter = group.textfields.begin(); for (TextfieldMap::const_iterator iter = group.textfields.begin();
iter != group.textfields.end(); ++iter) { iter != group.textfields.end(); ++iter) {
if (!iter->first->editable)
continue;
detail_outputs[iter->first] = iter->second->textfield()->text(); detail_outputs[iter->first] = iter->second->textfield()->text();
field_map[iter->first->type] = base::Bind( field_map[iter->first->type] = base::Bind(
&AutofillDialogViews::SetValidityForInput<DecoratedTextfield>, &AutofillDialogViews::SetValidityForInput<DecoratedTextfield>,
...@@ -1627,6 +1632,9 @@ bool AutofillDialogViews::ValidateGroup( ...@@ -1627,6 +1632,9 @@ bool AutofillDialogViews::ValidateGroup(
} }
for (ComboboxMap::const_iterator iter = group.comboboxes.begin(); for (ComboboxMap::const_iterator iter = group.comboboxes.begin();
iter != group.comboboxes.end(); ++iter) { iter != group.comboboxes.end(); ++iter) {
if (!iter->first->editable)
continue;
views::Combobox* combobox = iter->second; views::Combobox* combobox = iter->second;
string16 item = string16 item =
combobox->model()->GetItemAt(combobox->selected_index()); combobox->model()->GetItemAt(combobox->selected_index());
......
...@@ -1157,6 +1157,7 @@ ...@@ -1157,6 +1157,7 @@
'../base/base.gyp:base_i18n', '../base/base.gyp:base_i18n',
'../base/base.gyp:test_support_base', '../base/base.gyp:test_support_base',
'../components/components.gyp:autofill_risk_proto', '../components/components.gyp:autofill_risk_proto',
'../components/components.gyp:autofill_test_util',
'../device/device.gyp:device_bluetooth_mocks', '../device/device.gyp:device_bluetooth_mocks',
'../net/net.gyp:net', '../net/net.gyp:net',
'../net/net.gyp:net_test_support', '../net/net.gyp:net_test_support',
......
...@@ -413,6 +413,7 @@ ...@@ -413,6 +413,7 @@
'<@(chromium_child_dependencies)', '<@(chromium_child_dependencies)',
# 2) test-specific support libraries: # 2) test-specific support libraries:
'../base/base.gyp:test_support_base', '../base/base.gyp:test_support_base',
'../components/components.gyp:autofill_test_util',
'../media/media.gyp:media_test_support', '../media/media.gyp:media_test_support',
'../net/net.gyp:net', '../net/net.gyp:net',
'../net/net.gyp:net_test_support', '../net/net.gyp:net_test_support',
...@@ -1774,8 +1775,6 @@ ...@@ -1774,8 +1775,6 @@
'../components/autofill/browser/wallet/wallet_items_unittest.cc', '../components/autofill/browser/wallet/wallet_items_unittest.cc',
'../components/autofill/browser/wallet/wallet_service_url_unittest.cc', '../components/autofill/browser/wallet/wallet_service_url_unittest.cc',
'../components/autofill/browser/wallet/wallet_signin_helper_unittest.cc', '../components/autofill/browser/wallet/wallet_signin_helper_unittest.cc',
'../components/autofill/browser/wallet/wallet_test_util.cc',
'../components/autofill/browser/wallet/wallet_test_util.h',
# TODO(caitkp): Move to //components/components.gypi once # TODO(caitkp): Move to //components/components.gypi once
# remaining dependencies back to //chrome are eliminated. # remaining dependencies back to //chrome are eliminated.
......
...@@ -35,6 +35,15 @@ ...@@ -35,6 +35,15 @@
}, },
'includes': [ '../build/protoc.gypi' ] 'includes': [ '../build/protoc.gypi' ]
}, },
{
'target_name': 'autofill_test_util',
'type': 'static_library',
'sources': [
'autofill/browser/wallet/wallet_test_util.cc',
'autofill/browser/wallet/wallet_test_util.h',
],
'include_dirs': [ '..' ],
},
], ],
'conditions': [ 'conditions': [
['OS != "ios"', { ['OS != "ios"', {
......
...@@ -324,8 +324,7 @@ base::string16 WalletItems::MaskedInstrument::GetInfo( ...@@ -324,8 +324,7 @@ base::string16 WalletItems::MaskedInstrument::GetInfo(
return address().recipient_name(); return address().recipient_name();
case CREDIT_CARD_NUMBER: case CREDIT_CARD_NUMBER:
// TODO(dbeam): show these as XXXX-#### and non-editable in the UI. return DisplayName();
return last_four_digits();
case CREDIT_CARD_EXP_4_DIGIT_YEAR: case CREDIT_CARD_EXP_4_DIGIT_YEAR:
return base::IntToString16(expiration_year()); return base::IntToString16(expiration_year());
......
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