Commit b5093a79 authored by estade@chromium.org's avatar estade@chromium.org

allow wallet items to appear in requestAutocomplete UI

this CL adds a glue interface so that AutofillDialogController can be more agnostic about where its data is coming from.

BUG=174937
TBR=thestig@chromium.org
committed

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182348 0039d316-1c4b-4281-b951-d872f2087c98
parent 17d81248
......@@ -5,6 +5,7 @@
#include "chrome/browser/autofill/wallet/wallet_address.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
namespace wallet {
......@@ -62,6 +63,37 @@ scoped_ptr<base::DictionaryValue> Address::ToDictionaryWithoutID() const {
return dict.Pass();
}
string16 Address::DisplayName() const {
// TODO(estade): improve this stub implementation.
return UTF8ToUTF16(recipient_name() + ", " + address_line_1());
}
string16 Address::GetInfo(AutofillFieldType type) const {
switch (type) {
case NAME_FULL:
return UTF8ToUTF16(recipient_name());
case ADDRESS_HOME_LINE1:
return UTF8ToUTF16(address_line_1());
case ADDRESS_HOME_LINE2:
return UTF8ToUTF16(address_line_2());
case ADDRESS_HOME_CITY:
return UTF8ToUTF16(locality_name());
case ADDRESS_HOME_STATE:
return UTF8ToUTF16(admin_area_name());
case ADDRESS_HOME_ZIP:
return UTF8ToUTF16(postal_code_number());
// TODO(estade): implement more.
default:
NOTREACHED();
return string16();
}
}
scoped_ptr<Address>
Address::CreateAddressWithID(const base::DictionaryValue& dictionary) {
......
......@@ -9,6 +9,8 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "chrome/browser/autofill/field_types.h"
namespace base {
class DictionaryValue;
......@@ -87,6 +89,13 @@ class Address {
// sent to the server in a slightly different format.
scoped_ptr<base::DictionaryValue> ToDictionaryWithoutID() const;
// Returns a string that summarizes this address, suitable for display to
// the user.
string16 DisplayName() const;
// Returns data appropriate for |type|.
string16 GetInfo(AutofillFieldType type) const;
// Returns an empty scoped_ptr if input is invalid or a valid address that is
// selectable for Google Wallet use.
static scoped_ptr<Address>
......
......@@ -7,6 +7,7 @@
#include <string>
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
......@@ -21,6 +22,7 @@
#include "chrome/browser/autofill/wallet/wallet_service_url.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/autofill/autofill_dialog_view.h"
#include "chrome/browser/ui/autofill/data_model_wrapper.h"
#include "chrome/common/form_data.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
......@@ -378,24 +380,51 @@ string16 AutofillDialogControllerImpl::SuggestionTextForSection(
if (section == SECTION_EMAIL)
return model->GetLabelAt(model->checked_item());
if (section == SECTION_CC) {
scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
return wrapper->GetDisplayText();
}
scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
DialogSection section) {
SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
std::string item_key = model->GetItemKeyAt(model->checked_item());
scoped_ptr<DataModelWrapper> wrapper;
if (item_key.empty())
return wrapper.Pass();
if (CanPayWithWallet()) {
int index;
bool success = !base::StringToInt(item_key, &index);
DCHECK(success);
if (section == SECTION_CC) {
wrapper.reset(
new WalletInstrumentWrapper(wallet_items_->instruments()[index]));
} else {
wrapper.reset(
new WalletAddressWrapper(wallet_items_->addresses()[index]));
}
} else if (section == SECTION_CC) {
CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
return card->TypeAndLastFourDigits();
DCHECK(card);
wrapper.reset(new AutofillCreditCardWrapper(card));
} else {
// Calculate the variant by looking at how many items come from the same
// FormGroup. TODO(estade): add a test for this.
size_t variant = 0;
for (int i = model->checked_item() - 1; i >= 0; --i) {
if (model->GetItemKeyAt(i) == item_key)
variant++;
else
break;
}
AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
DCHECK(profile);
wrapper.reset(new AutofillDataModelWrapper(profile, variant));
}
const std::string app_locale = AutofillCountry::ApplicationLocale();
AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
string16 comma = ASCIIToUTF16(", ");
string16 label = profile->GetInfo(NAME_FULL, app_locale) +
comma + profile->GetInfo(ADDRESS_HOME_LINE1, app_locale);
string16 address2 = profile->GetInfo(ADDRESS_HOME_LINE2, app_locale);
if (!address2.empty())
label += comma + address2;
label += ASCIIToUTF16("\n") +
profile->GetInfo(ADDRESS_HOME_CITY, app_locale) + comma +
profile->GetInfo(ADDRESS_HOME_STATE, app_locale) + ASCIIToUTF16(" ") +
profile->GetInfo(ADDRESS_HOME_ZIP, app_locale);
return label;
return wrapper.Pass();
}
gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
......@@ -403,13 +432,11 @@ gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
if (section != SECTION_CC)
return gfx::Image();
std::string item_key =
suggested_cc_.GetItemKeyAt(suggested_cc_.checked_item());
if (item_key.empty())
scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
if (!model.get())
return gfx::Image();
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
return rb.GetImageNamed(card->IconResourceId());
return model->GetIcon();
}
void AutofillDialogControllerImpl::EditClickedForSection(
......@@ -424,14 +451,8 @@ void AutofillDialogControllerImpl::EditClickedForSection(
(*inputs)[0].autofilled_value = model->GetLabelAt(model->checked_item());
} else {
std::string guid = model->GetItemKeyAt(model->checked_item());
DCHECK(!guid.empty());
FormGroup* form_group = section == SECTION_CC ?
static_cast<FormGroup*>(GetManager()->GetCreditCardByGUID(guid)) :
static_cast<FormGroup*>(GetManager()->GetProfileByGUID(guid));
DCHECK(form_group);
FillInputFromFormGroup(form_group, inputs);
scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
model->FillInputs(inputs);
}
section_editing_state_[section] = true;
......@@ -720,6 +741,8 @@ void AutofillDialogControllerImpl::OnDidGetWalletItems(
WalletRequestCompleted(true);
if (items_changed) {
GenerateSuggestionsModels();
view_->ModelChanged();
view_->UpdateAccountChooser();
view_->UpdateNotificationArea();
}
......@@ -830,6 +853,8 @@ void AutofillDialogControllerImpl::WalletRequestCompleted(bool success) {
if (!success) {
had_wallet_error_ = true;
wallet_items_.reset();
GenerateSuggestionsModels();
view_->ModelChanged();
view_->UpdateAccountChooser();
view_->UpdateNotificationArea();
return;
......@@ -845,30 +870,49 @@ void AutofillDialogControllerImpl::GenerateSuggestionsModels() {
suggested_email_.Reset();
suggested_shipping_.Reset();
PersonalDataManager* manager = GetManager();
const std::vector<CreditCard*>& cards = manager->credit_cards();
for (size_t i = 0; i < cards.size(); ++i) {
suggested_cc_.AddKeyedItem(cards[i]->guid(), cards[i]->Label());
}
const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
const std::string app_locale = AutofillCountry::ApplicationLocale();
for (size_t i = 0; i < profiles.size(); ++i) {
if (!IsCompleteProfile(*profiles[i]))
continue;
// Add all email addresses.
std::vector<string16> values;
profiles[i]->GetMultiInfo(EMAIL_ADDRESS, app_locale, &values);
for (size_t j = 0; j < values.size(); ++j) {
if (!values[j].empty())
suggested_email_.AddKeyedItem(profiles[i]->guid(), values[j]);
if (CanPayWithWallet()) {
if (wallet_items_.get()) {
// TODO(estade): seems we need to hardcode the email address.
// TODO(estade): CC and billing need to be combined into one section,
// and suggestions added here.
const std::vector<wallet::Address*>& addresses =
wallet_items_->addresses();
for (size_t i = 0; i < addresses.size(); ++i) {
suggested_billing_.AddKeyedItem(base::IntToString(i),
addresses[i]->DisplayName());
suggested_shipping_.AddKeyedItem(base::IntToString(i),
addresses[i]->DisplayName());
}
}
} else {
PersonalDataManager* manager = GetManager();
const std::vector<CreditCard*>& cards = manager->credit_cards();
for (size_t i = 0; i < cards.size(); ++i) {
suggested_cc_.AddKeyedItem(cards[i]->guid(), cards[i]->Label());
}
// Don't add variants for addresses: the email variants are handled above,
// name is part of credit card and we'll just ignore phone number variants.
suggested_billing_.AddKeyedItem(profiles[i]->guid(), profiles[i]->Label());
suggested_shipping_.AddKeyedItem(profiles[i]->guid(), profiles[i]->Label());
const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
const std::string app_locale = AutofillCountry::ApplicationLocale();
for (size_t i = 0; i < profiles.size(); ++i) {
if (!IsCompleteProfile(*profiles[i]))
continue;
// Add all email addresses.
std::vector<string16> values;
profiles[i]->GetMultiInfo(EMAIL_ADDRESS, app_locale, &values);
for (size_t j = 0; j < values.size(); ++j) {
if (!values[j].empty())
suggested_email_.AddKeyedItem(profiles[i]->guid(), values[j]);
}
// Don't add variants for addresses: the email variants are handled above,
// name is part of credit card and we'll just ignore phone number
// variants.
suggested_billing_.AddKeyedItem(profiles[i]->guid(),
profiles[i]->Label());
suggested_shipping_.AddKeyedItem(profiles[i]->guid(),
profiles[i]->Label());
}
}
suggested_email_.AddKeyedItem(
......@@ -902,25 +946,12 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
DialogSection section,
const InputFieldComparator& compare) {
SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
std::string guid = model->GetItemKeyAt(model->checked_item());
PersonalDataManager* manager = GetManager();
if (!guid.empty() && !section_editing_state_[section]) {
FormGroup* form_group = section == SECTION_CC ?
static_cast<FormGroup*>(manager->GetCreditCardByGUID(guid)) :
static_cast<FormGroup*>(manager->GetProfileByGUID(guid));
DCHECK(form_group);
// Calculate the variant by looking at how many items come from the same
// FormGroup. TODO(estade): add a test for this.
size_t variant = 0;
for (int i = model->checked_item() - 1; i >= 0; --i) {
if (model->GetItemKeyAt(i) == guid)
variant++;
else
break;
}
FillFormStructureForSection(*form_group, variant, section, compare);
std::string item_key = model->GetItemKeyAt(model->checked_item());
if (!item_key.empty() && !section_editing_state_[section]) {
scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
// Only fill in data that is associated with this section.
const DetailInputs& inputs = RequestedFieldsForSection(section);
model->FillFormStructure(inputs, compare, &form_structure_);
// CVC needs special-casing because the CreditCard class doesn't store
// or handle them.
......@@ -928,6 +959,7 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
SetCvcResult(view_->GetCvc());
} else {
// The user manually input data.
PersonalDataManager* manager = GetManager();
DetailOutputMap output;
view_->GetUserInput(section, &output);
......
......@@ -40,6 +40,7 @@ class WebContents;
namespace autofill {
class AutofillDialogView;
class DataModelWrapper;
// This class drives the dialog that appears when a site uses the imperative
// autocomplete API to fill out a form.
......@@ -149,10 +150,6 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
virtual void OnPersonalDataChanged() OVERRIDE;
private:
// Determines whether |input| and |field| match.
typedef base::Callback<bool(const DetailInput& input,
const AutofillField& field)> InputFieldComparator;
// Refresh wallet items immediately if there's no refresh currently in
// progress, otherwise wait until the current refresh completes.
void ScheduleRefreshWalletItems();
......@@ -180,6 +177,11 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// menu.
bool IsCompleteProfile(const AutofillProfile& profile);
// Creates a DataModelWrapper item for the item that's checked in the
// suggestion model for |section|. This may represent Autofill
// data or Wallet data, depending on whether Wallet is currently enabled.
scoped_ptr<DataModelWrapper> CreateWrapper(DialogSection section);
// Fills in |section|-related fields in |output_| according to the state of
// |view_|.
void FillOutputForSection(DialogSection section);
......
......@@ -8,10 +8,13 @@
#include <map>
#include <vector>
#include "base/callback_forward.h"
#include "base/string16.h"
#include "chrome/browser/autofill/field_types.h"
#include "third_party/skia/include/core/SkColor.h"
class AutofillField;
namespace autofill {
// This struct describes a single input control for the imperative autocomplete
......@@ -34,6 +37,10 @@ struct DetailInput {
string16 autofilled_value;
};
// Determines whether |input| and |field| match.
typedef base::Callback<bool(const DetailInput& input,
const AutofillField& field)> InputFieldComparator;
// Sections of the dialog --- all fields that may be shown to the user fit under
// one of these sections.
enum DialogSection {
......
......@@ -219,6 +219,8 @@
'browser/ui/autofill/autofill_popup_controller_impl.h',
'browser/ui/autofill/autofill_popup_delegate.h',
'browser/ui/autofill/autofill_popup_view.h',
'browser/ui/autofill/data_model_wrapper.cc',
'browser/ui/autofill/data_model_wrapper.h',
'browser/ui/autofill/tab_autofill_manager_delegate.cc',
'browser/ui/autofill/tab_autofill_manager_delegate.h',
'browser/ui/auto_login_info_bar_delegate.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