autofill_dialog_controller_impl.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright 2013 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/autofill/autofill_dialog_controller_impl.h"
6
7#include <algorithm>
8#include <string>
9
10#include "base/bind.h"
11#include "base/logging.h"
12#include "base/prefs/pref_service.h"
13#include "base/string_number_conversions.h"
14#include "base/string_util.h"
15#include "base/strings/string_split.h"
16#include "base/time.h"
17#include "base/utf_string_conversions.h"
18#include "chrome/browser/autofill/personal_data_manager_factory.h"
19#include "chrome/browser/browser_process.h"
20#include "chrome/browser/extensions/shell_window_registry.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/ui/autofill/autofill_dialog_view.h"
23#include "chrome/browser/ui/autofill/data_model_wrapper.h"
24#include "chrome/browser/ui/base_window.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_finder.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/extensions/native_app_window.h"
29#include "chrome/browser/ui/extensions/shell_window.h"
30#include "chrome/common/chrome_version_info.h"
31#include "chrome/common/pref_names.h"
32#include "components/autofill/browser/autofill_country.h"
33#include "components/autofill/browser/autofill_manager.h"
34#include "components/autofill/browser/autofill_type.h"
35#include "components/autofill/browser/personal_data_manager.h"
36#include "components/autofill/browser/risk/fingerprint.h"
37#include "components/autofill/browser/risk/proto/fingerprint.pb.h"
38#include "components/autofill/browser/validation.h"
39#include "components/autofill/browser/wallet/cart.h"
40#include "components/autofill/browser/wallet/full_wallet.h"
41#include "components/autofill/browser/wallet/instrument.h"
42#include "components/autofill/browser/wallet/wallet_address.h"
43#include "components/autofill/browser/wallet/wallet_items.h"
44#include "components/autofill/browser/wallet/wallet_service_url.h"
45#include "components/autofill/common/form_data.h"
46#include "components/user_prefs/pref_registry_syncable.h"
47#include "content/public/browser/navigation_controller.h"
48#include "content/public/browser/navigation_details.h"
49#include "content/public/browser/navigation_entry.h"
50#include "content/public/browser/notification_service.h"
51#include "content/public/browser/notification_types.h"
52#include "content/public/browser/web_contents.h"
53#include "content/public/browser/web_contents_view.h"
54#include "content/public/common/url_constants.h"
55#include "grit/chromium_strings.h"
56#include "grit/generated_resources.h"
57#include "grit/theme_resources.h"
58#include "grit/webkit_resources.h"
59#include "net/base/cert_status_flags.h"
60#include "ui/base/l10n/l10n_util.h"
61#include "ui/base/resource/resource_bundle.h"
62#include "ui/gfx/canvas.h"
63#include "ui/gfx/color_utils.h"
64#include "ui/gfx/skbitmap_operations.h"
65
66namespace autofill {
67
68namespace {
69
70const bool kPayWithoutWalletDefault = false;
71
72// This is a pseudo-scientifically chosen maximum amount we want a fronting
73// (proxy) card to be able to charge. The current actual max is $2000. Using
74// only $1850 leaves some room for tax and shipping, etc. TODO(dbeam): send a
75// special value to the server to just ask for the maximum so we don't need to
76// hardcode it here (http://crbug.com/180731). TODO(dbeam): also maybe allow
77// users to give us this number via an <input> (http://crbug.com/180733).
78const int kCartMax = 1850;
79const char kCartCurrency[] = "USD";
80
81// Returns true if |input| should be shown when |field| has been requested.
82bool InputTypeMatchesFieldType(const DetailInput& input,
83                               const AutofillField& field) {
84  // If any credit card expiration info is asked for, show both month and year
85  // inputs.
86  if (field.type() == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
87      field.type() == CREDIT_CARD_EXP_2_DIGIT_YEAR ||
88      field.type() == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR ||
89      field.type() == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR ||
90      field.type() == CREDIT_CARD_EXP_MONTH) {
91    return input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
92           input.type == CREDIT_CARD_EXP_MONTH;
93  }
94
95  if (field.type() == CREDIT_CARD_TYPE)
96    return input.type == CREDIT_CARD_NUMBER;
97
98  return input.type == field.type();
99}
100
101// Returns true if |input| should be used for a site-requested |field|.
102bool DetailInputMatchesField(const DetailInput& input,
103                             const AutofillField& field) {
104  bool right_section = !input.section_suffix ||
105      EndsWith(field.section(), input.section_suffix, false);
106  return InputTypeMatchesFieldType(input, field) && right_section;
107}
108
109bool IsCreditCardType(AutofillFieldType type) {
110  return AutofillType(type).group() == AutofillType::CREDIT_CARD;
111}
112
113// Returns true if |input| should be used to fill a site-requested |field| which
114// is notated with a "shipping" tag, for use when the user has decided to use
115// the billing address as the shipping address.
116bool DetailInputMatchesShippingField(const DetailInput& input,
117                                     const AutofillField& field) {
118  if (input.section_suffix &&
119      std::string(input.section_suffix) == "billing") {
120    return InputTypeMatchesFieldType(input, field);
121  }
122
123  if (field.type() == NAME_FULL)
124    return input.type == CREDIT_CARD_NAME;
125
126  return DetailInputMatchesField(input, field);
127}
128
129// Constructs |inputs| from template data.
130void BuildInputs(const DetailInput* input_template,
131                 size_t template_size,
132                 DetailInputs* inputs) {
133  for (size_t i = 0; i < template_size; ++i) {
134    const DetailInput* input = &input_template[i];
135    inputs->push_back(*input);
136  }
137}
138
139// Uses |group| to fill in the |autofilled_value| for all inputs in |all_inputs|
140// (an out-param).
141void FillInputFromFormGroup(FormGroup* group, DetailInputs* inputs) {
142  const std::string app_locale = AutofillCountry::ApplicationLocale();
143  for (size_t j = 0; j < inputs->size(); ++j) {
144    (*inputs)[j].autofilled_value =
145        group->GetInfo((*inputs)[j].type, app_locale);
146  }
147}
148
149// Initializes |form_group| from user-entered data.
150void FillFormGroupFromOutputs(const DetailOutputMap& detail_outputs,
151                              FormGroup* form_group) {
152  for (DetailOutputMap::const_iterator iter = detail_outputs.begin();
153       iter != detail_outputs.end(); ++iter) {
154    if (!iter->second.empty())
155      form_group->SetRawInfo(iter->first->type, iter->second);
156  }
157}
158
159// Get billing info from |output| and put it into |card|, |cvc|, and |profile|.
160// These outparams are required because |card|/|profile| accept different types
161// of raw info, and CreditCard doesn't save CVCs.
162void GetBillingInfoFromOutputs(const DetailOutputMap& output,
163                               CreditCard* card,
164                               string16* cvc,
165                               AutofillProfile* profile) {
166  for (DetailOutputMap::const_iterator it = output.begin();
167       it != output.end(); ++it) {
168    string16 trimmed;
169    TrimWhitespace(it->second, TRIM_ALL, &trimmed);
170
171    // Special case CVC as CreditCard just swallows it.
172    if (it->first->type == CREDIT_CARD_VERIFICATION_CODE) {
173      cvc->assign(trimmed);
174    } else {
175      // Copy the credit card name to |profile| in addition to |card| as
176      // wallet::Instrument requires a recipient name for its billing address.
177      if (it->first->type == CREDIT_CARD_NAME)
178        profile->SetRawInfo(NAME_FULL, trimmed);
179
180      if (IsCreditCardType(it->first->type))
181        card->SetRawInfo(it->first->type, trimmed);
182      else
183        profile->SetRawInfo(it->first->type, trimmed);
184    }
185  }
186}
187
188// Returns the containing window for the given |web_contents|.  The containing
189// window might be a browser window for a Chrome tab, or it might be a shell
190// window for a platform app.
191BaseWindow* GetBaseWindowForWebContents(
192    const content::WebContents* web_contents) {
193  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
194  if (browser)
195    return browser->window();
196
197  gfx::NativeWindow native_window =
198      web_contents->GetView()->GetTopLevelNativeWindow();
199  ShellWindow* shell_window =
200      extensions::ShellWindowRegistry::
201          GetShellWindowForNativeWindowAnyProfile(native_window);
202  return shell_window->GetBaseWindow();
203}
204
205// Extracts the string value of a field with |type| from |output|. This is
206// useful when you only need the value of 1 input from a section of view inputs.
207string16 GetValueForType(const DetailOutputMap& output,
208                         AutofillFieldType type) {
209  for (DetailOutputMap::const_iterator it = output.begin();
210       it != output.end(); ++it) {
211    if (it->first->type == type)
212      return it->second;
213  }
214  NOTREACHED();
215  return string16();
216}
217
218}  // namespace
219
220AutofillDialogController::~AutofillDialogController() {}
221
222AutofillDialogControllerImpl::AutofillDialogControllerImpl(
223    content::WebContents* contents,
224    const FormData& form,
225    const GURL& source_url,
226    const AutofillMetrics& metric_logger,
227    DialogType dialog_type,
228    const base::Callback<void(const FormStructure*)>& callback)
229    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
230      contents_(contents),
231      form_structure_(form, std::string()),
232      invoked_from_same_origin_(true),
233      source_url_(source_url),
234      ssl_status_(form.ssl_status),
235      callback_(callback),
236      ALLOW_THIS_IN_INITIALIZER_LIST(
237          account_chooser_model_(this, profile_->GetPrefs())),
238      ALLOW_THIS_IN_INITIALIZER_LIST(
239          wallet_client_(profile_->GetRequestContext(), this)),
240      ALLOW_THIS_IN_INITIALIZER_LIST(suggested_email_(this)),
241      ALLOW_THIS_IN_INITIALIZER_LIST(suggested_cc_(this)),
242      ALLOW_THIS_IN_INITIALIZER_LIST(suggested_billing_(this)),
243      ALLOW_THIS_IN_INITIALIZER_LIST(suggested_cc_billing_(this)),
244      ALLOW_THIS_IN_INITIALIZER_LIST(suggested_shipping_(this)),
245      section_showing_popup_(SECTION_BILLING),
246      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
247      metric_logger_(metric_logger),
248      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
249      dialog_type_(dialog_type),
250      did_submit_(false),
251      autocheckout_is_running_(false),
252      had_autocheckout_error_(false) {
253  // TODO(estade): remove duplicates from |form|?
254}
255
256AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
257  if (popup_controller_)
258    popup_controller_->Hide();
259
260  metric_logger_.LogDialogInitialUserState(dialog_type_, initial_user_state_);
261}
262
263// static
264void AutofillDialogControllerImpl::RegisterUserPrefs(
265    PrefRegistrySyncable* registry) {
266  registry->RegisterBooleanPref(prefs::kAutofillDialogPayWithoutWallet,
267                                kPayWithoutWalletDefault,
268                                PrefRegistrySyncable::SYNCABLE_PREF);
269}
270
271void AutofillDialogControllerImpl::Show() {
272  dialog_shown_timestamp_ = base::Time::Now();
273
274  content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
275  const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
276  invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
277
278  // Log any relevant security exceptions.
279  metric_logger_.LogDialogSecurityMetric(
280      dialog_type_,
281      AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
282
283  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
284    metric_logger_.LogDialogSecurityMetric(
285        dialog_type_,
286        AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
287  }
288
289  if (!invoked_from_same_origin_) {
290    metric_logger_.LogDialogSecurityMetric(
291        dialog_type_,
292        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
293  }
294
295  // Determine what field types should be included in the dialog.
296  bool has_types = false;
297  bool has_sections = false;
298  form_structure_.ParseFieldTypesFromAutocompleteAttributes(&has_types,
299                                                            &has_sections);
300  // Fail if the author didn't specify autocomplete types.
301  if (!has_types) {
302    callback_.Run(NULL);
303    delete this;
304    return;
305  }
306
307  const DetailInput kEmailInputs[] = {
308    { 1, EMAIL_ADDRESS, IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL },
309  };
310
311  const DetailInput kCCInputs[] = {
312    { 2, CREDIT_CARD_NUMBER, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER },
313    { 3, CREDIT_CARD_EXP_MONTH },
314    { 3, CREDIT_CARD_EXP_4_DIGIT_YEAR },
315    { 3, CREDIT_CARD_VERIFICATION_CODE, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC },
316    { 4, CREDIT_CARD_NAME, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARDHOLDER_NAME },
317  };
318
319  const DetailInput kBillingInputs[] = {
320    { 5, ADDRESS_HOME_LINE1, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_1,
321      "billing" },
322    { 6, ADDRESS_HOME_LINE2, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_2,
323      "billing" },
324    { 7, ADDRESS_HOME_CITY, IDS_AUTOFILL_DIALOG_PLACEHOLDER_LOCALITY,
325      "billing" },
326    // TODO(estade): state placeholder should depend on locale.
327    { 8, ADDRESS_HOME_STATE, IDS_AUTOFILL_FIELD_LABEL_STATE, "billing" },
328    { 8, ADDRESS_HOME_ZIP, IDS_AUTOFILL_DIALOG_PLACEHOLDER_POSTAL_CODE,
329      "billing", 0.5 },
330    // TODO(estade): this should have a default based on the locale.
331    { 9, ADDRESS_HOME_COUNTRY, 0, "billing" },
332    { 10, PHONE_HOME_WHOLE_NUMBER,
333      IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER, "billing" },
334  };
335
336  const DetailInput kShippingInputs[] = {
337    { 11, NAME_FULL, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESSEE_NAME,
338      "shipping" },
339    { 12, ADDRESS_HOME_LINE1, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_1,
340      "shipping" },
341    { 13, ADDRESS_HOME_LINE2, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_2,
342      "shipping" },
343    { 14, ADDRESS_HOME_CITY, IDS_AUTOFILL_DIALOG_PLACEHOLDER_LOCALITY,
344      "shipping" },
345    { 15, ADDRESS_HOME_STATE, IDS_AUTOFILL_FIELD_LABEL_STATE, "shipping" },
346    { 15, ADDRESS_HOME_ZIP, IDS_AUTOFILL_DIALOG_PLACEHOLDER_POSTAL_CODE,
347      "shipping", 0.5 },
348    { 16, ADDRESS_HOME_COUNTRY, 0, "shipping" },
349    { 17, PHONE_HOME_WHOLE_NUMBER,
350      IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER, "shipping" },
351  };
352
353  BuildInputs(kEmailInputs,
354              arraysize(kEmailInputs),
355              &requested_email_fields_);
356
357  BuildInputs(kCCInputs,
358              arraysize(kCCInputs),
359              &requested_cc_fields_);
360
361  BuildInputs(kBillingInputs,
362              arraysize(kBillingInputs),
363              &requested_billing_fields_);
364
365  BuildInputs(kCCInputs,
366              arraysize(kCCInputs),
367              &requested_cc_billing_fields_);
368  BuildInputs(kBillingInputs,
369              arraysize(kBillingInputs),
370              &requested_cc_billing_fields_);
371
372  BuildInputs(kShippingInputs,
373              arraysize(kShippingInputs),
374              &requested_shipping_fields_);
375
376  GenerateSuggestionsModels();
377
378  // TODO(estade): don't show the dialog if the site didn't specify the right
379  // fields. First we must figure out what the "right" fields are.
380  view_.reset(CreateView());
381  view_->Show();
382  GetManager()->AddObserver(this);
383
384  // Request sugar info after the view is showing to simplify code for now.
385  if (IsPayingWithWallet()) {
386    // TODO(dbeam): Add Risk capabilites once the UI supports risk challenges.
387    GetWalletClient()->GetWalletItems(
388        source_url_,
389        std::vector<wallet::WalletClient::RiskCapability>());
390  }
391}
392
393void AutofillDialogControllerImpl::Hide() {
394  if (view_)
395    view_->Hide();
396}
397
398void AutofillDialogControllerImpl::UpdateProgressBar(double value) {
399  view_->UpdateProgressBar(value);
400}
401
402void AutofillDialogControllerImpl::OnAutocheckoutError() {
403  had_autocheckout_error_ = true;
404  autocheckout_is_running_ = false;
405  view_->UpdateNotificationArea();
406  view_->UpdateButtonStrip();
407}
408
409////////////////////////////////////////////////////////////////////////////////
410// AutofillDialogController implementation.
411
412string16 AutofillDialogControllerImpl::DialogTitle() const {
413  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE);
414}
415
416string16 AutofillDialogControllerImpl::EditSuggestionText() const {
417  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT);
418}
419
420string16 AutofillDialogControllerImpl::UseBillingForShippingText() const {
421  return l10n_util::GetStringUTF16(
422      IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING);
423}
424
425string16 AutofillDialogControllerImpl::CancelButtonText() const {
426  return l10n_util::GetStringUTF16(IDS_CANCEL);
427}
428
429string16 AutofillDialogControllerImpl::ConfirmButtonText() const {
430  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON);
431}
432
433string16 AutofillDialogControllerImpl::CancelSignInText() const {
434  // TODO(abodenha): real strings and l10n.
435  return ASCIIToUTF16("Don't sign in.");
436}
437
438string16 AutofillDialogControllerImpl::SaveLocallyText() const {
439  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX);
440}
441
442string16 AutofillDialogControllerImpl::ProgressBarText() const {
443  return l10n_util::GetStringUTF16(
444      IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_PROGRESS_BAR);
445}
446
447DialogSignedInState AutofillDialogControllerImpl::SignedInState() const {
448  if (!wallet_items_)
449    return REQUIRES_RESPONSE;
450
451  if (wallet_items_->HasRequiredAction(wallet::GAIA_AUTH))
452    return REQUIRES_SIGN_IN;
453
454  if (wallet_items_->HasRequiredAction(wallet::PASSIVE_GAIA_AUTH))
455    return REQUIRES_PASSIVE_SIGN_IN;
456
457  return SIGNED_IN;
458}
459
460string16 AutofillDialogControllerImpl::AccountChooserText() const {
461  if (!IsPayingWithWallet())
462    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PAY_WITHOUT_WALLET);
463
464  // TODO(dbeam): real strings and l10n.
465  if (SignedInState() == SIGNED_IN)
466    return ASCIIToUTF16("user@example.com");
467
468  // In this case, the account chooser should be showing the signin link.
469  return string16();
470}
471
472string16 AutofillDialogControllerImpl::SignInLinkText() const {
473  // TODO(estade): real strings and l10n.
474  return ASCIIToUTF16("Sign in to use Google Wallet");
475}
476
477bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
478  return !IsPayingWithWallet();
479}
480
481bool AutofillDialogControllerImpl::AutocheckoutIsRunning() const {
482  return autocheckout_is_running_;
483}
484
485bool AutofillDialogControllerImpl::HadAutocheckoutError() const {
486  return had_autocheckout_error_;
487}
488
489bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
490    ui::DialogButton button) const {
491  if (button == ui::DIALOG_BUTTON_OK)
492    return !did_submit_;
493  DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button);
494  // TODO(ahutter): Make it possible for the user to cancel out of the dialog
495  // while Autocheckout is in progress.
496  return had_autocheckout_error_ || !did_submit_;
497}
498
499bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section)
500    const {
501  if (IsPayingWithWallet())
502    return section != SECTION_BILLING && section != SECTION_CC;
503
504  return section != SECTION_CC_BILLING;
505}
506
507bool AutofillDialogControllerImpl::HasCompleteWallet() const {
508  return wallet_items_.get() != NULL &&
509         !wallet_items_->instruments().empty() &&
510         !wallet_items_->addresses().empty();
511}
512
513const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection(
514    DialogSection section) const {
515  switch (section) {
516    case SECTION_EMAIL:
517      return requested_email_fields_;
518    case SECTION_CC:
519      return requested_cc_fields_;
520    case SECTION_BILLING:
521      return requested_billing_fields_;
522    case SECTION_CC_BILLING:
523      return requested_cc_billing_fields_;
524    case SECTION_SHIPPING:
525      return requested_shipping_fields_;
526  }
527
528  NOTREACHED();
529  return requested_billing_fields_;
530}
531
532ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
533    AutofillFieldType type) {
534  switch (type) {
535    case CREDIT_CARD_EXP_MONTH:
536      return &cc_exp_month_combobox_model_;
537
538    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
539      return &cc_exp_year_combobox_model_;
540
541    case ADDRESS_HOME_COUNTRY:
542      return &country_combobox_model_;
543
544    default:
545      return NULL;
546  }
547}
548
549ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSection(
550    DialogSection section) {
551  return SuggestionsMenuModelForSection(section);
552}
553
554ui::MenuModel* AutofillDialogControllerImpl::MenuModelForAccountChooser() {
555  // When paying with wallet, but not signed in, there is no menu, just a
556  // sign in link.
557  if (IsPayingWithWallet() && SignedInState() != SIGNED_IN)
558    return NULL;
559
560  return &account_chooser_model_;
561}
562
563gfx::Image AutofillDialogControllerImpl::AccountChooserImage() {
564  if (!MenuModelForAccountChooser()) {
565    return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
566        IDR_WALLET_ICON);
567  }
568
569  gfx::Image icon;
570  account_chooser_model_.GetIconAt(account_chooser_model_.checked_item(),
571                                   &icon);
572  return icon;
573}
574
575string16 AutofillDialogControllerImpl::LabelForSection(DialogSection section)
576    const {
577  switch (section) {
578    case SECTION_EMAIL:
579      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_EMAIL);
580    case SECTION_CC:
581      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC);
582    case SECTION_BILLING:
583      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING);
584    case SECTION_CC_BILLING:
585      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC_BILLING);
586    case SECTION_SHIPPING:
587      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING);
588    default:
589      NOTREACHED();
590      return string16();
591  }
592}
593
594string16 AutofillDialogControllerImpl::SuggestionTextForSection(
595    DialogSection section) {
596  // When the user has clicked 'edit', don't show a suggestion (even though
597  // there is a profile selected in the model).
598  if (section_editing_state_[section])
599    return string16();
600
601  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
602  std::string item_key = model->GetItemKeyForCheckedItem();
603  if (item_key.empty())
604    return string16();
605
606  if (section == SECTION_EMAIL)
607    return model->GetLabelAt(model->checked_item());
608
609  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
610  return wrapper->GetDisplayText();
611}
612
613scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
614    DialogSection section) {
615  if (IsPayingWithWallet() && full_wallet_) {
616    if (section == SECTION_CC_BILLING) {
617      return scoped_ptr<DataModelWrapper>(
618          new FullWalletBillingWrapper(full_wallet_.get()));
619    }
620    if (section == SECTION_SHIPPING) {
621      return scoped_ptr<DataModelWrapper>(
622          new FullWalletShippingWrapper(full_wallet_.get()));
623    }
624  }
625
626  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
627  std::string item_key = model->GetItemKeyForCheckedItem();
628  if (item_key.empty())
629    return scoped_ptr<DataModelWrapper>();
630
631  if (IsPayingWithWallet()) {
632    int index;
633    bool success = base::StringToInt(item_key, &index);
634    DCHECK(success);
635
636    if (section == SECTION_CC_BILLING) {
637      return scoped_ptr<DataModelWrapper>(
638          new WalletInstrumentWrapper(wallet_items_->instruments()[index]));
639    }
640    // TODO(dbeam): should SECTION_EMAIL get here?
641    return scoped_ptr<DataModelWrapper>(
642        new WalletAddressWrapper(wallet_items_->addresses()[index]));
643  }
644
645  if (section == SECTION_CC) {
646    CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
647    DCHECK(card);
648    return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card));
649  }
650
651  // Calculate the variant by looking at how many items come from the same
652  // FormGroup.
653  size_t variant = 0;
654  for (int i = model->checked_item() - 1; i >= 0; --i) {
655    if (model->GetItemKeyAt(i) == item_key)
656      variant++;
657    else
658      break;
659  }
660
661  AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
662  DCHECK(profile);
663  return scoped_ptr<DataModelWrapper>(
664      new AutofillProfileWrapper(profile, variant));
665}
666
667gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
668    DialogSection section) {
669  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
670  if (!model.get())
671    return gfx::Image();
672
673  return model->GetIcon();
674}
675
676void AutofillDialogControllerImpl::EditClickedForSection(
677    DialogSection section) {
678  DetailInputs* inputs = MutableRequestedFieldsForSection(section);
679  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
680  model->FillInputs(inputs);
681  section_editing_state_[section] = true;
682  view_->UpdateSection(section);
683}
684
685gfx::Image AutofillDialogControllerImpl::IconForField(
686    AutofillFieldType type, const string16& user_input) const {
687  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
688  if (type == CREDIT_CARD_VERIFICATION_CODE)
689    return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT);
690
691  // For the credit card, we show a few grayscale images, and possibly one
692  // color image if |user_input| is a valid card number.
693  if (type == CREDIT_CARD_NUMBER) {
694    const int card_idrs[] = {
695      IDR_AUTOFILL_CC_VISA,
696      IDR_AUTOFILL_CC_MASTERCARD,
697      IDR_AUTOFILL_CC_AMEX,
698      IDR_AUTOFILL_CC_DISCOVER
699    };
700    const int number_of_cards = arraysize(card_idrs);
701    // The number of pixels between card icons.
702    const int kCardPadding = 2;
703
704    gfx::ImageSkia some_card = *rb.GetImageSkiaNamed(card_idrs[0]);
705    const int card_width = some_card.width();
706    gfx::Canvas canvas(
707        gfx::Size((card_width + kCardPadding) * number_of_cards - kCardPadding,
708                  some_card.height()),
709        ui::SCALE_FACTOR_100P,
710        true);
711    CreditCard card;
712    card.SetRawInfo(CREDIT_CARD_NUMBER, user_input);
713
714    for (int i = 0; i < number_of_cards; ++i) {
715      int idr = card_idrs[i];
716      gfx::ImageSkia card_image = *rb.GetImageSkiaNamed(idr);
717      if (card.IconResourceId() != idr) {
718        color_utils::HSL shift = {-1, 0, 0.8};
719        SkBitmap disabled_bitmap =
720            SkBitmapOperations::CreateHSLShiftedBitmap(*card_image.bitmap(),
721                                                       shift);
722        card_image = gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap);
723      }
724
725      canvas.DrawImageInt(card_image, i * (card_width + kCardPadding), 0);
726    }
727
728    gfx::ImageSkia skia(canvas.ExtractImageRep());
729    return gfx::Image(skia);
730  }
731
732  return gfx::Image();
733}
734
735bool AutofillDialogControllerImpl::InputIsValid(AutofillFieldType type,
736                                                const string16& value) {
737  switch (type) {
738    case EMAIL_ADDRESS:
739      return IsValidEmailAddress(value);
740
741    case CREDIT_CARD_NUMBER:
742      return autofill::IsValidCreditCardNumber(value);
743    case CREDIT_CARD_NAME:
744      break;
745    case CREDIT_CARD_EXP_MONTH:
746    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
747      break;
748    case CREDIT_CARD_VERIFICATION_CODE:
749      return autofill::IsValidCreditCardSecurityCode(value);
750
751    case ADDRESS_HOME_LINE1:
752      break;
753    case ADDRESS_HOME_LINE2:
754      return true;  // Line 2 is optional - always valid.
755    case ADDRESS_HOME_CITY:
756    case ADDRESS_HOME_STATE:
757    case ADDRESS_HOME_ZIP:
758    case ADDRESS_HOME_COUNTRY:
759      break;
760
761    case NAME_FULL:  // Used for shipping.
762      break;
763
764    case PHONE_HOME_WHOLE_NUMBER:  // Used in billing section.
765      // TODO(dbeam): validate with libphonenumber.
766      break;
767
768    default:
769      NOTREACHED();  // Trying to validate unknown field.
770      break;
771  }
772
773  return !value.empty();
774}
775
776std::vector<AutofillFieldType> AutofillDialogControllerImpl::InputsAreValid(
777    const DetailOutputMap& inputs) {
778  std::vector<AutofillFieldType> invalid_fields;
779  std::map<AutofillFieldType, string16> field_values;
780  for (DetailOutputMap::const_iterator iter = inputs.begin();
781       iter != inputs.end(); ++iter) {
782    field_values[iter->first->type] = iter->second;
783    if (!InputIsValid(iter->first->type, iter->second))
784      invalid_fields.push_back(iter->first->type);
785  }
786
787  // If the dialog has a CC month, there must 4 digit year.
788  // Validate the date formed by month and appropriate year field.
789  if (field_values.count(CREDIT_CARD_EXP_MONTH)) {
790    DCHECK(field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
791    if (!autofill::IsValidCreditCardExpirationDate(
792            field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
793            field_values[CREDIT_CARD_EXP_MONTH],
794            base::Time::Now())) {
795      invalid_fields.push_back(CREDIT_CARD_EXP_MONTH);
796      invalid_fields.push_back(CREDIT_CARD_EXP_4_DIGIT_YEAR);
797    }
798  }
799
800  // If there is a credit card number and a CVC, validate them together.
801  if (field_values.count(CREDIT_CARD_NUMBER) &&
802      field_values.count(CREDIT_CARD_VERIFICATION_CODE)) {
803    if (!autofill::IsValidCreditCardSecurityCode(
804            field_values[CREDIT_CARD_VERIFICATION_CODE],
805            field_values[CREDIT_CARD_NUMBER])) {
806      invalid_fields.push_back(CREDIT_CARD_VERIFICATION_CODE);
807    }
808  }
809
810  // De-duplicate invalid fields.
811  std::sort(invalid_fields.begin(), invalid_fields.end());
812  invalid_fields.erase(std::unique(
813      invalid_fields.begin(), invalid_fields.end()), invalid_fields.end());
814
815  return invalid_fields;
816}
817
818void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
819    const DetailInput* input,
820    DialogSection section,
821    gfx::NativeView parent_view,
822    const gfx::Rect& content_bounds,
823    const string16& field_contents,
824    bool was_edit) {
825  // If the field is edited down to empty, don't show a popup.
826  if (was_edit && field_contents.empty()) {
827    HidePopup();
828    return;
829  }
830
831  // If the user clicks while the popup is already showing, be sure to hide
832  // it.
833  if (!was_edit && popup_controller_) {
834    HidePopup();
835    return;
836  }
837
838  std::vector<string16> popup_values, popup_labels, popup_icons;
839  if (section == SECTION_CC) {
840    GetManager()->GetCreditCardSuggestions(input->type,
841                                           field_contents,
842                                           &popup_values,
843                                           &popup_labels,
844                                           &popup_icons,
845                                           &popup_guids_);
846  } else {
847    // TODO(estade): add field types from email section?
848    const DetailInputs& inputs = RequestedFieldsForSection(section);
849    std::vector<AutofillFieldType> field_types;
850    field_types.reserve(inputs.size());
851    for (DetailInputs::const_iterator iter = inputs.begin();
852         iter != inputs.end(); ++iter) {
853      field_types.push_back(iter->type);
854    }
855    GetManager()->GetProfileSuggestions(input->type,
856                                        field_contents,
857                                        false,
858                                        field_types,
859                                        &popup_values,
860                                        &popup_labels,
861                                        &popup_icons,
862                                        &popup_guids_);
863  }
864
865  if (popup_values.empty())
866    return;
867
868  // TODO(estade): do we need separators and control rows like 'Clear
869  // Form'?
870  std::vector<int> popup_ids;
871  for (size_t i = 0; i < popup_guids_.size(); ++i) {
872    popup_ids.push_back(i);
873  }
874
875  popup_controller_ = AutofillPopupControllerImpl::GetOrCreate(
876      popup_controller_, this, parent_view, content_bounds);
877  popup_controller_->Show(popup_values,
878                          popup_labels,
879                          popup_icons,
880                          popup_ids);
881  section_showing_popup_ = section;
882}
883
884void AutofillDialogControllerImpl::FocusMoved() {
885  HidePopup();
886}
887
888void AutofillDialogControllerImpl::ViewClosed() {
889  GetManager()->RemoveObserver(this);
890
891  if (autocheckout_is_running_ || had_autocheckout_error_) {
892    AutofillMetrics::AutocheckoutCompletionStatus metric =
893        autocheckout_is_running_ ?
894            AutofillMetrics::AUTOCHECKOUT_SUCCEEDED :
895            AutofillMetrics::AUTOCHECKOUT_FAILED;
896    metric_logger_.LogAutocheckoutDuration(
897        base::Time::Now() - autocheckout_started_timestamp_,
898        metric);
899  }
900
901  // On a successful submit, save the payment type for next time. If there
902  // was a Wallet server error, try to pay with Wallet again next time.
903  // Reset the view so that updates to the pref aren't processed.
904  view_.reset();
905  bool pay_without_wallet = !IsPayingWithWallet() &&
906      !account_chooser_model_.had_wallet_error();
907  profile_->GetPrefs()->SetBoolean(prefs::kAutofillDialogPayWithoutWallet,
908                                   pay_without_wallet);
909
910  delete this;
911}
912
913std::vector<DialogNotification>
914    AutofillDialogControllerImpl::CurrentNotifications() const {
915  std::vector<DialogNotification> notifications;
916
917  if (IsPayingWithWallet()) {
918    // TODO(dbeam): what about REQUIRES_PASSIVE_SIGN_IN?
919    if (SignedInState() == SIGNED_IN) {
920      // On first run with a complete wallet profile, show a notification
921      // explaining where this data came from.
922      if (IsFirstRun() && HasCompleteWallet()) {
923        notifications.push_back(
924            DialogNotification(
925                DialogNotification::EXPLANATORY_MESSAGE,
926                l10n_util::GetStringUTF16(
927                    IDS_AUTOFILL_DIALOG_DETAILS_FROM_WALLET)));
928      } else {
929        notifications.push_back(
930            DialogNotification(
931                DialogNotification::WALLET_PROMO,
932                l10n_util::GetStringUTF16(
933                    IDS_AUTOFILL_DIALOG_SAVE_DETAILS_IN_WALLET)));
934      }
935    } else if (IsFirstRun()) {
936      // If the user is not signed in, show an upsell notification on first run.
937      notifications.push_back(
938          DialogNotification(
939              DialogNotification::WALLET_PROMO,
940              l10n_util::GetStringUTF16(
941                  IDS_AUTOFILL_DIALOG_SIGN_IN_AND_SAVE_DETAILS)));
942    }
943  }
944
945  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
946    notifications.push_back(
947        DialogNotification(
948            DialogNotification::SECURITY_WARNING,
949            l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECURITY_WARNING)));
950  }
951
952  if (!invoked_from_same_origin_) {
953    notifications.push_back(
954        DialogNotification(
955            DialogNotification::SECURITY_WARNING,
956            l10n_util::GetStringFUTF16(
957                IDS_AUTOFILL_DIALOG_SITE_WARNING,
958                UTF8ToUTF16(source_url_.host()))));
959  }
960
961  if (had_autocheckout_error_) {
962    notifications.push_back(
963        DialogNotification(
964            DialogNotification::AUTOCHECKOUT_ERROR,
965            l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_ERROR)));
966  }
967
968  if (account_chooser_model_.had_wallet_error()) {
969    // TODO(dbeam): pass along the Wallet error or remove from the translation.
970    // TODO(dbeam): figure out a way to dismiss this error after a while.
971    notifications.push_back(DialogNotification(
972        DialogNotification::WALLET_ERROR,
973        l10n_util::GetStringFUTF16(
974            IDS_AUTOFILL_DIALOG_COMPLETE_WITHOUT_WALLET,
975            ASCIIToUTF16("Oops, [Wallet-Error]."))));
976  }
977
978  return notifications;
979}
980
981void AutofillDialogControllerImpl::StartSignInFlow() {
982  DCHECK(registrar_.IsEmpty());
983
984  content::Source<content::NavigationController> source(view_->ShowSignIn());
985  registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
986}
987
988void AutofillDialogControllerImpl::EndSignInFlow() {
989  DCHECK(!registrar_.IsEmpty());
990  registrar_.RemoveAll();
991  view_->HideSignIn();
992}
993
994void AutofillDialogControllerImpl::OnCancel() {
995  if (!did_submit_) {
996    metric_logger_.LogDialogUiDuration(
997        base::Time::Now() - dialog_shown_timestamp_,
998        dialog_type_,
999        AutofillMetrics::DIALOG_CANCELED);
1000  }
1001
1002  // If Autocheckout has an error, it's possible that the dialog will be
1003  // submitted to start the flow and then cancelled to close the dialog after
1004  // the error.
1005  if (!callback_.is_null()) {
1006    callback_.Run(NULL);
1007    callback_ = base::Callback<void(const FormStructure*)>();
1008  }
1009}
1010
1011void AutofillDialogControllerImpl::OnSubmit() {
1012  did_submit_ = true;
1013  metric_logger_.LogDialogUiDuration(
1014      base::Time::Now() - dialog_shown_timestamp_,
1015      dialog_type_,
1016      AutofillMetrics::DIALOG_ACCEPTED);
1017
1018  if (dialog_type_ == DIALOG_TYPE_AUTOCHECKOUT) {
1019    // Stop observing PersonalDataManager to avoid the dialog redrawing while
1020    // in an Autocheckout flow.
1021    GetManager()->RemoveObserver(this);
1022    autocheckout_is_running_ = true;
1023    autocheckout_started_timestamp_ = base::Time::Now();
1024    view_->UpdateButtonStrip();
1025  }
1026
1027  if (IsPayingWithWallet())
1028    SubmitWithWallet();
1029  else
1030    FinishSubmit();
1031}
1032
1033Profile* AutofillDialogControllerImpl::profile() {
1034  return profile_;
1035}
1036
1037content::WebContents* AutofillDialogControllerImpl::web_contents() {
1038  return contents_;
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042// AutofillPopupDelegate implementation.
1043
1044void AutofillDialogControllerImpl::OnPopupShown(
1045    content::KeyboardListener* listener) {
1046  metric_logger_.LogDialogPopupEvent(
1047      dialog_type_, AutofillMetrics::DIALOG_POPUP_SHOWN);
1048}
1049
1050void AutofillDialogControllerImpl::OnPopupHidden(
1051    content::KeyboardListener* listener) {}
1052
1053void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier) {
1054  // TODO(estade): implement.
1055}
1056
1057void AutofillDialogControllerImpl::DidAcceptSuggestion(const string16& value,
1058                                                       int identifier) {
1059  const PersonalDataManager::GUIDPair& pair = popup_guids_[identifier];
1060
1061  FormGroup* form_group = section_showing_popup_ == SECTION_CC ?
1062      static_cast<FormGroup*>(GetManager()->GetCreditCardByGUID(pair.first)) :
1063      // TODO(estade): need to use the variant, |pair.second|.
1064      static_cast<FormGroup*>(GetManager()->GetProfileByGUID(pair.first));
1065  DCHECK(form_group);
1066
1067  FillInputFromFormGroup(
1068      form_group,
1069      MutableRequestedFieldsForSection(section_showing_popup_));
1070  view_->UpdateSection(section_showing_popup_);
1071
1072  metric_logger_.LogDialogPopupEvent(
1073      dialog_type_, AutofillMetrics::DIALOG_POPUP_FORM_FILLED);
1074
1075  // TODO(estade): not sure why it's necessary to do this explicitly.
1076  HidePopup();
1077}
1078
1079void AutofillDialogControllerImpl::RemoveSuggestion(const string16& value,
1080                                                    int identifier) {
1081  // TODO(estade): implement.
1082}
1083
1084void AutofillDialogControllerImpl::ClearPreviewedForm() {
1085  // TODO(estade): implement.
1086}
1087
1088////////////////////////////////////////////////////////////////////////////////
1089// content::NotificationObserver implementation.
1090
1091void AutofillDialogControllerImpl::Observe(
1092    int type,
1093    const content::NotificationSource& source,
1094    const content::NotificationDetails& details) {
1095  DCHECK_EQ(type, content::NOTIFICATION_NAV_ENTRY_COMMITTED);
1096  content::LoadCommittedDetails* load_details =
1097      content::Details<content::LoadCommittedDetails>(details).ptr();
1098  if (wallet::IsSignInContinueUrl(load_details->entry->GetVirtualURL())) {
1099    EndSignInFlow();
1100    if (IsPayingWithWallet()) {
1101      // TODO(dbeam): Add Risk capabilites once the UI supports risk challenges.
1102      GetWalletClient()->GetWalletItems(
1103          source_url_,
1104          std::vector<wallet::WalletClient::RiskCapability>());
1105    }
1106  }
1107}
1108
1109////////////////////////////////////////////////////////////////////////////////
1110// SuggestionsMenuModelDelegate implementation.
1111
1112void AutofillDialogControllerImpl::SuggestionItemSelected(
1113    const SuggestionsMenuModel& model) {
1114  DialogSection section = SectionForSuggestionsMenuModel(model);
1115  section_editing_state_[section] = false;
1116  view_->UpdateSection(section);
1117}
1118
1119////////////////////////////////////////////////////////////////////////////////
1120// wallet::WalletClientDelegate implementation.
1121
1122const AutofillMetrics& AutofillDialogControllerImpl::GetMetricLogger() const {
1123  return metric_logger_;
1124}
1125
1126DialogType AutofillDialogControllerImpl::GetDialogType() const {
1127  return dialog_type_;
1128}
1129
1130std::string AutofillDialogControllerImpl::GetRiskData() const {
1131  // TODO(dbeam): Implement this.
1132  return "risky business";
1133}
1134
1135void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
1136}
1137
1138void AutofillDialogControllerImpl::OnDidAuthenticateInstrument(bool success) {
1139  // TODO(dbeam): use the returned full wallet. b/8332329
1140  if (success)
1141    GetFullWallet();
1142  else
1143    DisableWallet();
1144}
1145
1146void AutofillDialogControllerImpl::OnDidGetFullWallet(
1147    scoped_ptr<wallet::FullWallet> full_wallet) {
1148  // TODO(dbeam): handle more required actions.
1149  full_wallet_ = full_wallet.Pass();
1150
1151  if (full_wallet_->HasRequiredAction(wallet::VERIFY_CVV))
1152    DisableWallet();
1153  else
1154    FinishSubmit();
1155}
1156
1157void AutofillDialogControllerImpl::OnDidGetWalletItems(
1158    scoped_ptr<wallet::WalletItems> wallet_items) {
1159  // TODO(dbeam): verify all items support kCartCurrency?
1160  wallet_items_ = wallet_items.Pass();
1161  GenerateSuggestionsModels();
1162  view_->ModelChanged();
1163  view_->UpdateAccountChooser();
1164  view_->UpdateNotificationArea();
1165
1166  // On the first successful response, compute the initial user state metric.
1167  if (initial_user_state_ == AutofillMetrics::DIALOG_USER_STATE_UNKNOWN)
1168    initial_user_state_ = GetInitialUserState();
1169}
1170
1171void AutofillDialogControllerImpl::OnDidSaveAddress(
1172    const std::string& address_id,
1173    const std::vector<wallet::RequiredAction>& required_actions) {
1174  // TODO(dbeam): handle required actions.
1175  active_address_id_ = address_id;
1176  GetFullWallet();
1177}
1178
1179void AutofillDialogControllerImpl::OnDidSaveInstrument(
1180    const std::string& instrument_id,
1181    const std::vector<wallet::RequiredAction>& required_actions) {
1182  // TODO(dbeam): handle required actions.
1183  active_instrument_id_ = instrument_id;
1184  GetFullWallet();
1185}
1186
1187void AutofillDialogControllerImpl::OnDidSaveInstrumentAndAddress(
1188    const std::string& instrument_id,
1189    const std::string& address_id,
1190    const std::vector<wallet::RequiredAction>& required_actions) {
1191  // TODO(dbeam): handle required actions.
1192  active_instrument_id_ = instrument_id;
1193  active_address_id_ = address_id;
1194  GetFullWallet();
1195}
1196
1197void AutofillDialogControllerImpl::OnDidSendAutocheckoutStatus() {
1198  NOTIMPLEMENTED();
1199}
1200
1201void AutofillDialogControllerImpl::OnDidUpdateAddress(
1202    const std::string& address_id,
1203    const std::vector<wallet::RequiredAction>& required_actions) {
1204  // TODO(dbeam): Handle this callback.
1205  NOTIMPLEMENTED() << " address_id=" << address_id;
1206}
1207
1208void AutofillDialogControllerImpl::OnDidUpdateInstrument(
1209    const std::string& instrument_id,
1210    const std::vector<wallet::RequiredAction>& required_actions) {
1211  // TODO(dbeam): handle required actions.
1212}
1213
1214void AutofillDialogControllerImpl::OnWalletError(
1215    wallet::WalletClient::ErrorType error_type) {
1216  // TODO(dbeam): Do something with |error_type|.
1217  DisableWallet();
1218}
1219
1220void AutofillDialogControllerImpl::OnMalformedResponse() {
1221  DisableWallet();
1222}
1223
1224void AutofillDialogControllerImpl::OnNetworkError(int response_code) {
1225  DisableWallet();
1226}
1227
1228////////////////////////////////////////////////////////////////////////////////
1229// PersonalDataManagerObserver implementation.
1230
1231void AutofillDialogControllerImpl::OnPersonalDataChanged() {
1232  HidePopup();
1233  GenerateSuggestionsModels();
1234  view_->ModelChanged();
1235}
1236
1237void AutofillDialogControllerImpl::AccountChoiceChanged() {
1238  if (!view_)
1239    return;
1240
1241  GenerateSuggestionsModels();
1242  view_->ModelChanged();
1243  view_->UpdateAccountChooser();
1244  view_->UpdateNotificationArea();
1245
1246  if (IsPayingWithWallet() && !wallet_items_) {
1247    // TODO(dbeam): Add Risk capabilites once the UI supports risk challenges.
1248    GetWalletClient()->GetWalletItems(
1249        source_url_,
1250        std::vector<wallet::WalletClient::RiskCapability>());
1251  }
1252}
1253
1254////////////////////////////////////////////////////////////////////////////////
1255
1256bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
1257    const content::NativeWebKeyboardEvent& event) {
1258  if (popup_controller_)
1259    return popup_controller_->HandleKeyPressEvent(event);
1260
1261  return false;
1262}
1263
1264bool AutofillDialogControllerImpl::RequestingCreditCardInfo() const {
1265  DCHECK_GT(form_structure_.field_count(), 0U);
1266
1267  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1268    if (IsCreditCardType(form_structure_.field(i)->type()))
1269      return true;
1270  }
1271
1272  return false;
1273}
1274
1275bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
1276  return source_url_.SchemeIs(chrome::kHttpsScheme) &&
1277         !net::IsCertStatusError(ssl_status_.cert_status) &&
1278         !net::IsCertStatusMinorError(ssl_status_.cert_status);
1279}
1280
1281AutofillDialogView* AutofillDialogControllerImpl::CreateView() {
1282  return AutofillDialogView::Create(this);
1283}
1284
1285PersonalDataManager* AutofillDialogControllerImpl::GetManager() {
1286  return PersonalDataManagerFactory::GetForProfile(profile_);
1287}
1288
1289wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient() {
1290  return &wallet_client_;
1291}
1292
1293bool AutofillDialogControllerImpl::IsPayingWithWallet() const {
1294  return account_chooser_model_.WalletIsSelected();
1295}
1296
1297void AutofillDialogControllerImpl::DisableWallet() {
1298  account_chooser_model_.SetHadWalletError();
1299  GetWalletClient()->CancelPendingRequests();
1300  wallet_items_.reset();
1301
1302  GenerateSuggestionsModels();
1303  if (view_) {
1304    view_->ModelChanged();
1305    view_->UpdateNotificationArea();
1306  }
1307}
1308
1309bool AutofillDialogControllerImpl::IsFirstRun() const {
1310  PrefService* prefs = profile_->GetPrefs();
1311  return !prefs->HasPrefPath(prefs::kAutofillDialogPayWithoutWallet);
1312}
1313
1314void AutofillDialogControllerImpl::GenerateSuggestionsModels() {
1315  suggested_email_.Reset();
1316  suggested_cc_.Reset();
1317  suggested_billing_.Reset();
1318  suggested_cc_billing_.Reset();
1319  suggested_shipping_.Reset();
1320
1321  if (IsPayingWithWallet()) {
1322    if (wallet_items_.get()) {
1323      // TODO(estade): seems we need to hardcode the email address.
1324
1325      const std::vector<wallet::Address*>& addresses =
1326          wallet_items_->addresses();
1327      for (size_t i = 0; i < addresses.size(); ++i) {
1328        // TODO(dbeam): respect wallet_items_->default_instrument_id().
1329        suggested_shipping_.AddKeyedItemWithSublabel(
1330            base::IntToString(i),
1331            addresses[i]->DisplayName(),
1332            addresses[i]->DisplayNameDetail());
1333      }
1334
1335      const std::vector<wallet::WalletItems::MaskedInstrument*>& instruments =
1336          wallet_items_->instruments();
1337      for (size_t i = 0; i < instruments.size(); ++i) {
1338        // TODO(dbeam): respect wallet_items_->default_address_id().
1339        suggested_cc_billing_.AddKeyedItemWithSublabelAndIcon(
1340            base::IntToString(i),
1341            instruments[i]->DisplayName(),
1342            instruments[i]->DisplayNameDetail(),
1343            instruments[i]->CardIcon());
1344      }
1345    }
1346
1347    suggested_cc_billing_.AddKeyedItem(
1348        std::string(),
1349        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_DETAILS));
1350  } else {
1351    PersonalDataManager* manager = GetManager();
1352    const std::vector<CreditCard*>& cards = manager->credit_cards();
1353    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1354    for (size_t i = 0; i < cards.size(); ++i) {
1355      suggested_cc_.AddKeyedItemWithIcon(
1356          cards[i]->guid(),
1357          cards[i]->Label(),
1358          rb.GetImageNamed(cards[i]->IconResourceId()));
1359    }
1360
1361    const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
1362    const std::string app_locale = AutofillCountry::ApplicationLocale();
1363    for (size_t i = 0; i < profiles.size(); ++i) {
1364      if (!IsCompleteProfile(*profiles[i]))
1365        continue;
1366
1367      // Add all email addresses.
1368      std::vector<string16> values;
1369      profiles[i]->GetMultiInfo(EMAIL_ADDRESS, app_locale, &values);
1370      for (size_t j = 0; j < values.size(); ++j) {
1371        if (!values[j].empty())
1372          suggested_email_.AddKeyedItem(profiles[i]->guid(), values[j]);
1373      }
1374
1375      // Don't add variants for addresses: the email variants are handled above,
1376      // name is part of credit card and we'll just ignore phone number
1377      // variants.
1378      suggested_billing_.AddKeyedItem(profiles[i]->guid(),
1379                                      profiles[i]->Label());
1380      suggested_shipping_.AddKeyedItem(profiles[i]->guid(),
1381                                       profiles[i]->Label());
1382    }
1383
1384    suggested_cc_.AddKeyedItem(
1385        std::string(),
1386        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD));
1387    suggested_billing_.AddKeyedItem(
1388        std::string(),
1389        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS));
1390  }
1391
1392  suggested_email_.AddKeyedItem(
1393      std::string(),
1394      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_EMAIL_ADDRESS));
1395  suggested_shipping_.AddKeyedItem(
1396      std::string(),
1397      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS));
1398}
1399
1400bool AutofillDialogControllerImpl::IsCompleteProfile(
1401    const AutofillProfile& profile) {
1402  const std::string app_locale = AutofillCountry::ApplicationLocale();
1403  for (size_t i = 0; i < requested_shipping_fields_.size(); ++i) {
1404    AutofillFieldType type = requested_shipping_fields_[i].type;
1405    if (type != ADDRESS_HOME_LINE2 &&
1406        profile.GetInfo(type, app_locale).empty()) {
1407      return false;
1408    }
1409  }
1410
1411  return true;
1412}
1413
1414void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
1415    DialogSection section,
1416    const InputFieldComparator& compare) {
1417  if (!SectionIsActive(section))
1418    return;
1419
1420  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1421  if (wrapper) {
1422    // Only fill in data that is associated with this section.
1423    const DetailInputs& inputs = RequestedFieldsForSection(section);
1424    wrapper->FillFormStructure(inputs, compare, &form_structure_);
1425
1426    // CVC needs special-casing because the CreditCard class doesn't store or
1427    // handle them. This isn't necessary when filling the combined CC and
1428    // billing section as CVC comes from |full_wallet_| in this case.
1429    if (section == SECTION_CC)
1430      SetCvcResult(view_->GetCvc());
1431  } else {
1432    // The user manually input data. If using Autofill, save the info as new or
1433    // edited data. Always fill local data into |form_structure_|.
1434    DetailOutputMap output;
1435    view_->GetUserInput(section, &output);
1436
1437    if (section == SECTION_CC) {
1438      CreditCard card;
1439      FillFormGroupFromOutputs(output, &card);
1440
1441      if (ShouldSaveDetailsLocally())
1442        GetManager()->SaveImportedCreditCard(card);
1443
1444      FillFormStructureForSection(card, 0, section, compare);
1445
1446      // Again, CVC needs special-casing. Fill it in directly from |output|.
1447      SetCvcResult(GetValueForType(output, CREDIT_CARD_VERIFICATION_CODE));
1448    } else {
1449      AutofillProfile profile;
1450      FillFormGroupFromOutputs(output, &profile);
1451
1452      // TODO(estade): we should probably edit the existing profile in the
1453      // cases where the input data is based on an existing profile (user
1454      // clicked "Edit" or autofill popup filled in the form).
1455      if (ShouldSaveDetailsLocally())
1456        GetManager()->SaveImportedProfile(profile);
1457
1458      FillFormStructureForSection(profile, 0, section, compare);
1459    }
1460  }
1461}
1462
1463void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) {
1464  FillOutputForSectionWithComparator(section,
1465                                     base::Bind(DetailInputMatchesField));
1466}
1467
1468void AutofillDialogControllerImpl::FillFormStructureForSection(
1469    const FormGroup& form_group,
1470    size_t variant,
1471    DialogSection section,
1472    const InputFieldComparator& compare) {
1473  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1474    AutofillField* field = form_structure_.field(i);
1475    // Only fill in data that is associated with this section.
1476    const DetailInputs& inputs = RequestedFieldsForSection(section);
1477    for (size_t j = 0; j < inputs.size(); ++j) {
1478      if (compare.Run(inputs[j], *field)) {
1479        form_group.FillFormField(*field, variant, field);
1480        break;
1481      }
1482    }
1483  }
1484}
1485
1486void AutofillDialogControllerImpl::SetCvcResult(const string16& cvc) {
1487  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1488    AutofillField* field = form_structure_.field(i);
1489    if (field->type() == CREDIT_CARD_VERIFICATION_CODE) {
1490      field->value = cvc;
1491      break;
1492    }
1493  }
1494}
1495
1496SuggestionsMenuModel* AutofillDialogControllerImpl::
1497    SuggestionsMenuModelForSection(DialogSection section) {
1498  switch (section) {
1499    case SECTION_EMAIL:
1500      return &suggested_email_;
1501    case SECTION_CC:
1502      return &suggested_cc_;
1503    case SECTION_BILLING:
1504      return &suggested_billing_;
1505    case SECTION_SHIPPING:
1506      return &suggested_shipping_;
1507    case SECTION_CC_BILLING:
1508      return &suggested_cc_billing_;
1509  }
1510
1511  NOTREACHED();
1512  return NULL;
1513}
1514
1515DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
1516    const SuggestionsMenuModel& model) {
1517  if (&model == &suggested_email_)
1518    return SECTION_EMAIL;
1519
1520  if (&model == &suggested_cc_)
1521    return SECTION_CC;
1522
1523  if (&model == &suggested_billing_)
1524    return SECTION_BILLING;
1525
1526  if (&model == &suggested_cc_billing_)
1527    return SECTION_CC_BILLING;
1528
1529  DCHECK_EQ(&model, &suggested_shipping_);
1530  return SECTION_SHIPPING;
1531}
1532
1533DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
1534    DialogSection section) {
1535  return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
1536}
1537
1538void AutofillDialogControllerImpl::HidePopup() {
1539  if (popup_controller_)
1540    popup_controller_->Hide();
1541}
1542
1543void AutofillDialogControllerImpl::LoadRiskFingerprintData() {
1544  // TODO(dbeam): Add a CHECK or otherwise strong guarantee that the ToS have
1545  // been accepted prior to calling into this method.  Also, ensure that the UI
1546  // contains a clear indication to the user as to what data will be collected.
1547  // Until then, this code should not be called.
1548
1549  int64 gaia_id = 0;
1550  bool success =
1551      base::StringToInt64(wallet_items_->obfuscated_gaia_id(), &gaia_id);
1552  DCHECK(success);
1553
1554  gfx::Rect window_bounds =
1555      GetBaseWindowForWebContents(web_contents())->GetBounds();
1556
1557  PrefService* user_prefs = profile_->GetPrefs();
1558  std::string charset = user_prefs->GetString(prefs::kDefaultCharset);
1559  std::string accept_languages = user_prefs->GetString(prefs::kAcceptLanguages);
1560  base::Time install_time = base::Time::FromTimeT(
1561      g_browser_process->local_state()->GetInt64(prefs::kInstallDate));
1562
1563  risk::GetFingerprint(
1564      gaia_id, window_bounds, *web_contents(),
1565      chrome::VersionInfo().Version(), charset, accept_languages, install_time,
1566      base::Bind(&AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData,
1567                 weak_ptr_factory_.GetWeakPtr()));
1568}
1569
1570void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
1571    scoped_ptr<risk::Fingerprint> fingerprint) {
1572  NOTIMPLEMENTED();
1573}
1574
1575bool AutofillDialogControllerImpl::IsManuallyEditingSection(
1576    DialogSection section) {
1577  return section_editing_state_[section] ||
1578         SuggestionsMenuModelForSection(section)->
1579             GetItemKeyForCheckedItem().empty();
1580}
1581
1582bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
1583  // If the user is editing or inputting data, ask the view.
1584  if (IsManuallyEditingSection(SECTION_SHIPPING))
1585    return view_->UseBillingForShipping();
1586
1587  // Otherwise, the checkbox should be hidden so its state is irrelevant.
1588  // Always use the shipping suggestion model.
1589  return false;
1590}
1591
1592bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
1593  // It's possible that the user checked [X] Save details locally before
1594  // switching payment methods, so only ask the view whether to save details
1595  // locally if that checkbox is showing (currently if not paying with wallet).
1596  return !IsPayingWithWallet() && view_->SaveDetailsLocally();
1597}
1598
1599void AutofillDialogControllerImpl::SubmitWithWallet() {
1600  // TODO(dbeam): disallow interacting with the dialog while submitting.
1601
1602  active_instrument_id_.clear();
1603  active_address_id_.clear();
1604
1605  if (!section_editing_state_[SECTION_CC_BILLING]) {
1606    SuggestionsMenuModel* billing =
1607        SuggestionsMenuModelForSection(SECTION_CC_BILLING);
1608    if (!billing->GetItemKeyForCheckedItem().empty() &&
1609        billing->checked_item() <
1610            static_cast<int>(wallet_items_->instruments().size())) {
1611      const wallet::WalletItems::MaskedInstrument* active_instrument =
1612          wallet_items_->instruments()[billing->checked_item()];
1613      active_instrument_id_ = active_instrument->object_id();
1614
1615      // TODO(dbeam): does re-using instrument address IDs work?
1616      if (ShouldUseBillingForShipping())
1617        active_address_id_ = active_instrument->address().object_id();
1618    }
1619  }
1620
1621  if (!section_editing_state_[SECTION_SHIPPING] && active_address_id_.empty()) {
1622    SuggestionsMenuModel* shipping =
1623        SuggestionsMenuModelForSection(SECTION_SHIPPING);
1624    if (!shipping->GetItemKeyForCheckedItem().empty() &&
1625        shipping->checked_item() <
1626            static_cast<int>(wallet_items_->addresses().size())) {
1627      active_address_id_ =
1628          wallet_items_->addresses()[shipping->checked_item()]->object_id();
1629    }
1630  }
1631
1632  if (!wallet_items_->legal_documents().empty()) {
1633    std::vector<std::string> doc_ids;
1634    for (size_t i = 0; i < wallet_items_->legal_documents().size(); ++i) {
1635      doc_ids.push_back(wallet_items_->legal_documents()[i]->document_id());
1636    }
1637    GetWalletClient()->AcceptLegalDocuments(
1638        doc_ids, wallet_items_->google_transaction_id(), source_url_);
1639  }
1640
1641  scoped_ptr<wallet::Instrument> new_instrument;
1642  if (active_instrument_id_.empty()) {
1643    DetailOutputMap output;
1644    view_->GetUserInput(SECTION_CC_BILLING, &output);
1645
1646    CreditCard card;
1647    AutofillProfile profile;
1648    string16 cvc;
1649    GetBillingInfoFromOutputs(output, &card, &cvc, &profile);
1650
1651    new_instrument.reset(new wallet::Instrument(card, cvc, profile));
1652  }
1653
1654  scoped_ptr<wallet::Address> new_address;
1655  if (active_address_id_.empty()) {
1656    if (ShouldUseBillingForShipping() && new_instrument.get()) {
1657      new_address.reset(new wallet::Address(new_instrument->address()));
1658    } else {
1659      DetailOutputMap output;
1660      view_->GetUserInput(SECTION_SHIPPING, &output);
1661
1662      AutofillProfile profile;
1663      FillFormGroupFromOutputs(output, &profile);
1664
1665      new_address.reset(new wallet::Address(profile));
1666    }
1667  }
1668
1669  if (new_instrument.get() && new_address.get()) {
1670    GetWalletClient()->SaveInstrumentAndAddress(
1671        *new_instrument,
1672        *new_address,
1673        wallet_items_->obfuscated_gaia_id(),
1674        source_url_);
1675  } else if (new_instrument.get()) {
1676    GetWalletClient()->SaveInstrument(*new_instrument,
1677                                      wallet_items_->obfuscated_gaia_id(),
1678                                      source_url_);
1679  } else if (new_address.get()) {
1680    GetWalletClient()->SaveAddress(*new_address, source_url_);
1681  } else {
1682    GetFullWallet();
1683  }
1684}
1685
1686void AutofillDialogControllerImpl::GetFullWallet() {
1687  DCHECK(did_submit_);
1688  DCHECK(IsPayingWithWallet());
1689  DCHECK(wallet_items_);
1690  DCHECK(!active_instrument_id_.empty());
1691  DCHECK(!active_address_id_.empty());
1692
1693  GetWalletClient()->GetFullWallet(wallet::WalletClient::FullWalletRequest(
1694      active_instrument_id_,
1695      active_address_id_,
1696      source_url_,
1697      wallet::Cart(base::IntToString(kCartMax), kCartCurrency),
1698      wallet_items_->google_transaction_id(),
1699      std::vector<wallet::WalletClient::RiskCapability>()));
1700}
1701
1702void AutofillDialogControllerImpl::FinishSubmit() {
1703  FillOutputForSection(SECTION_EMAIL);
1704  FillOutputForSection(SECTION_CC);
1705  FillOutputForSection(SECTION_BILLING);
1706  FillOutputForSection(SECTION_CC_BILLING);
1707  if (ShouldUseBillingForShipping()) {
1708    FillOutputForSectionWithComparator(
1709        SECTION_BILLING,
1710        base::Bind(DetailInputMatchesShippingField));
1711    FillOutputForSectionWithComparator(
1712        SECTION_CC,
1713        base::Bind(DetailInputMatchesShippingField));
1714  } else {
1715    FillOutputForSection(SECTION_SHIPPING);
1716  }
1717  callback_.Run(&form_structure_);
1718  callback_ = base::Callback<void(const FormStructure*)>();
1719
1720  if (dialog_type_ == DIALOG_TYPE_REQUEST_AUTOCOMPLETE) {
1721    // This may delete us.
1722    Hide();
1723  }
1724}
1725
1726AutofillMetrics::DialogInitialUserStateMetric
1727    AutofillDialogControllerImpl::GetInitialUserState() const {
1728  // Consider a user to be an Autofill user if the user has any credit cards
1729  // or addresses saved.  Check that the item count is greater than 1 because
1730  // an "empty" menu still has the "add new" menu item.
1731  const bool has_autofill_profiles =
1732      suggested_cc_.GetItemCount() > 1 ||
1733      suggested_billing_.GetItemCount() > 1;
1734
1735  if (SignedInState() != SIGNED_IN) {
1736    // Not signed in.
1737    return has_autofill_profiles ?
1738        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL :
1739        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL;
1740  }
1741
1742  // Signed in.
1743  if (wallet_items_->instruments().empty()) {
1744    // No Wallet items.
1745    return has_autofill_profiles ?
1746        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL :
1747        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL;
1748  }
1749
1750  // Has Wallet items.
1751  return has_autofill_profiles ?
1752      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL :
1753      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL;
1754}
1755
1756}  // namespace autofill
1757