autofill_dialog_controller_impl.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 <map>
9#include <string>
10
11#include "base/base64.h"
12#include "base/bind.h"
13#include "base/logging.h"
14#include "base/prefs/pref_service.h"
15#include "base/string_util.h"
16#include "base/strings/string_number_conversions.h"
17#include "base/strings/string_split.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/time.h"
20#include "chrome/browser/autofill/personal_data_manager_factory.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/extensions/shell_window_registry.h"
23#include "chrome/browser/prefs/scoped_user_pref_update.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/ui/autofill/autofill_dialog_view.h"
26#include "chrome/browser/ui/autofill/data_model_wrapper.h"
27#include "chrome/browser/ui/browser.h"
28#include "chrome/browser/ui/browser_finder.h"
29#include "chrome/browser/ui/browser_navigator.h"
30#include "chrome/browser/ui/browser_window.h"
31#include "chrome/browser/ui/extensions/native_app_window.h"
32#include "chrome/browser/ui/extensions/shell_window.h"
33#include "chrome/common/chrome_version_info.h"
34#include "chrome/common/pref_names.h"
35#include "chrome/common/url_constants.h"
36#include "components/autofill/browser/autofill_country.h"
37#include "components/autofill/browser/autofill_data_model.h"
38#include "components/autofill/browser/autofill_manager.h"
39#include "components/autofill/browser/autofill_type.h"
40#include "components/autofill/browser/personal_data_manager.h"
41#include "components/autofill/browser/phone_number_i18n.h"
42#include "components/autofill/browser/validation.h"
43#include "components/autofill/common/form_data.h"
44#include "components/autofill/content/browser/risk/fingerprint.h"
45#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
46#include "components/autofill/content/browser/wallet/full_wallet.h"
47#include "components/autofill/content/browser/wallet/instrument.h"
48#include "components/autofill/content/browser/wallet/wallet_address.h"
49#include "components/autofill/content/browser/wallet/wallet_items.h"
50#include "components/autofill/content/browser/wallet/wallet_service_url.h"
51#include "components/autofill/content/browser/wallet/wallet_signin_helper.h"
52#include "components/user_prefs/pref_registry_syncable.h"
53#include "content/public/browser/browser_thread.h"
54#include "content/public/browser/geolocation_provider.h"
55#include "content/public/browser/navigation_controller.h"
56#include "content/public/browser/navigation_details.h"
57#include "content/public/browser/navigation_entry.h"
58#include "content/public/browser/notification_service.h"
59#include "content/public/browser/notification_types.h"
60#include "content/public/browser/web_contents.h"
61#include "content/public/browser/web_contents_view.h"
62#include "content/public/common/url_constants.h"
63#include "grit/chromium_strings.h"
64#include "grit/component_resources.h"
65#include "grit/generated_resources.h"
66#include "grit/theme_resources.h"
67#include "grit/webkit_resources.h"
68#include "net/cert/cert_status_flags.h"
69#include "ui/base/base_window.h"
70#include "ui/base/l10n/l10n_util.h"
71#include "ui/base/resource/resource_bundle.h"
72#include "ui/gfx/canvas.h"
73#include "ui/gfx/color_utils.h"
74#include "ui/gfx/skbitmap_operations.h"
75
76namespace autofill {
77
78namespace {
79
80const char kAddNewItemKey[] = "add-new-item";
81const char kManageItemsKey[] = "manage-items";
82const char kSameAsBillingKey[] = "same-as-billing";
83
84// Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
85// these values).
86const char kGuidPrefKey[] = "guid";
87const char kVariantPrefKey[] = "variant";
88
89// This string is stored along with saved addresses and credit cards in the
90// WebDB, and hence should not be modified, so that it remains consistent over
91// time.
92const char kAutofillDialogOrigin[] = "Chrome Autofill dialog";
93
94// HSL shift to gray out an image.
95const color_utils::HSL kGrayImageShift = {-1, 0, 0.8};
96
97// Returns true if |input| should be shown when |field_type| has been requested.
98bool InputTypeMatchesFieldType(const DetailInput& input,
99                               AutofillFieldType field_type) {
100  // If any credit card expiration info is asked for, show both month and year
101  // inputs.
102  if (field_type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
103      field_type == CREDIT_CARD_EXP_2_DIGIT_YEAR ||
104      field_type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR ||
105      field_type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR ||
106      field_type == CREDIT_CARD_EXP_MONTH) {
107    return input.type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
108           input.type == CREDIT_CARD_EXP_MONTH;
109  }
110
111  if (field_type == CREDIT_CARD_TYPE)
112    return input.type == CREDIT_CARD_NUMBER;
113
114  return input.type == field_type;
115}
116
117// Returns true if |input| should be used for a site-requested |field|.
118bool DetailInputMatchesField(const DetailInput& input,
119                             const AutofillField& field) {
120  return InputTypeMatchesFieldType(input, field.type());
121}
122
123bool IsCreditCardType(AutofillFieldType type) {
124  return AutofillType(type).group() == AutofillType::CREDIT_CARD;
125}
126
127// Returns true if |input| should be used to fill a site-requested |field| which
128// is notated with a "shipping" tag, for use when the user has decided to use
129// the billing address as the shipping address.
130bool DetailInputMatchesShippingField(const DetailInput& input,
131                                     const AutofillField& field) {
132  if (field.type() == NAME_FULL)
133    return input.type == CREDIT_CARD_NAME;
134
135  // Equivalent billing field type is used to support UseBillingAsShipping
136  // usecase.
137  AutofillFieldType field_type =
138      AutofillType::GetEquivalentBillingFieldType(field.type());
139
140  return InputTypeMatchesFieldType(input, field_type);
141}
142
143// Constructs |inputs| from template data.
144void BuildInputs(const DetailInput* input_template,
145                 size_t template_size,
146                 DetailInputs* inputs) {
147  for (size_t i = 0; i < template_size; ++i) {
148    const DetailInput* input = &input_template[i];
149    inputs->push_back(*input);
150  }
151}
152
153// Initializes |form_group| from user-entered data.
154void FillFormGroupFromOutputs(const DetailOutputMap& detail_outputs,
155                              FormGroup* form_group) {
156  for (DetailOutputMap::const_iterator iter = detail_outputs.begin();
157       iter != detail_outputs.end(); ++iter) {
158    if (!iter->second.empty()) {
159      AutofillFieldType type = iter->first->type;
160      if (type == ADDRESS_HOME_COUNTRY || type == ADDRESS_BILLING_COUNTRY) {
161        form_group->SetInfo(type,
162                            iter->second,
163                            g_browser_process->GetApplicationLocale());
164      } else {
165        form_group->SetRawInfo(iter->first->type, iter->second);
166      }
167    }
168  }
169}
170
171// Get billing info from |output| and put it into |card|, |cvc|, and |profile|.
172// These outparams are required because |card|/|profile| accept different types
173// of raw info, and CreditCard doesn't save CVCs.
174void GetBillingInfoFromOutputs(const DetailOutputMap& output,
175                               CreditCard* card,
176                               string16* cvc,
177                               AutofillProfile* profile) {
178  for (DetailOutputMap::const_iterator it = output.begin();
179       it != output.end(); ++it) {
180    string16 trimmed;
181    TrimWhitespace(it->second, TRIM_ALL, &trimmed);
182
183    // Special case CVC as CreditCard just swallows it.
184    if (it->first->type == CREDIT_CARD_VERIFICATION_CODE) {
185      if (cvc)
186        cvc->assign(trimmed);
187    } else if (it->first->type == ADDRESS_HOME_COUNTRY ||
188               it->first->type == ADDRESS_BILLING_COUNTRY) {
189        profile->SetInfo(it->first->type,
190                         trimmed,
191                         g_browser_process->GetApplicationLocale());
192    } else {
193      // Copy the credit card name to |profile| in addition to |card| as
194      // wallet::Instrument requires a recipient name for its billing address.
195      if (profile && it->first->type == CREDIT_CARD_NAME)
196        profile->SetRawInfo(NAME_FULL, trimmed);
197
198      if (IsCreditCardType(it->first->type)) {
199        if (card)
200          card->SetRawInfo(it->first->type, trimmed);
201      } else if (profile) {
202        profile->SetRawInfo(it->first->type, trimmed);
203      }
204    }
205  }
206}
207
208// Returns the containing window for the given |web_contents|. The containing
209// window might be a browser window for a Chrome tab, or it might be a shell
210// window for a platform app.
211ui::BaseWindow* GetBaseWindowForWebContents(
212    const content::WebContents* web_contents) {
213  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
214  if (browser)
215    return browser->window();
216
217  gfx::NativeWindow native_window =
218      web_contents->GetView()->GetTopLevelNativeWindow();
219  ShellWindow* shell_window =
220      extensions::ShellWindowRegistry::
221          GetShellWindowForNativeWindowAnyProfile(native_window);
222  return shell_window->GetBaseWindow();
223}
224
225// Extracts the string value of a field with |type| from |output|. This is
226// useful when you only need the value of 1 input from a section of view inputs.
227string16 GetValueForType(const DetailOutputMap& output,
228                         AutofillFieldType type) {
229  for (DetailOutputMap::const_iterator it = output.begin();
230       it != output.end(); ++it) {
231    if (it->first->type == type)
232      return it->second;
233  }
234  NOTREACHED();
235  return string16();
236}
237
238// Returns a string descriptor for a DialogSection, for use with prefs (do not
239// change these values).
240std::string SectionToPrefString(DialogSection section) {
241  switch (section) {
242    case SECTION_CC:
243      return "cc";
244
245    case SECTION_BILLING:
246      return "billing";
247
248    case SECTION_CC_BILLING:
249      // The SECTION_CC_BILLING section isn't active when using Autofill.
250      NOTREACHED();
251      return std::string();
252
253    case SECTION_SHIPPING:
254      return "shipping";
255
256    case SECTION_EMAIL:
257      return "email";
258  }
259
260  NOTREACHED();
261  return std::string();
262}
263
264// Check if a given MaskedInstrument is allowed for the purchase.
265bool IsInstrumentAllowed(
266    const wallet::WalletItems::MaskedInstrument& instrument) {
267  switch (instrument.status()) {
268    case wallet::WalletItems::MaskedInstrument::VALID:
269    case wallet::WalletItems::MaskedInstrument::PENDING:
270    case wallet::WalletItems::MaskedInstrument::EXPIRED:
271    case wallet::WalletItems::MaskedInstrument::BILLING_INCOMPLETE:
272      return true;
273    default:
274      return false;
275  }
276}
277
278// Signals that the user has opted in to geolocation services.  Factored out
279// into a separate method because all interaction with the geolocation provider
280// needs to happen on the IO thread, which is not the thread
281// AutofillDialogController lives on.
282void UserDidOptIntoLocationServices() {
283  content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
284}
285
286// Returns whether |data_model| is complete, i.e. can fill out all the
287// |requested_fields|, and verified, i.e. not just automatically aggregated.
288// Incomplete or unverifed data will not be displayed in the dropdown menu.
289bool HasCompleteAndVerifiedData(const AutofillDataModel& data_model,
290                                const DetailInputs& requested_fields) {
291  if (!data_model.IsVerified())
292    return false;
293
294  const std::string app_locale = g_browser_process->GetApplicationLocale();
295  for (size_t i = 0; i < requested_fields.size(); ++i) {
296    AutofillFieldType type = requested_fields[i].type;
297    if (type != ADDRESS_HOME_LINE2 &&
298        type != CREDIT_CARD_VERIFICATION_CODE &&
299        data_model.GetInfo(type, app_locale).empty()) {
300      return false;
301    }
302  }
303
304  return true;
305}
306
307// Loops through |addresses_| comparing to |address| ignoring ID. If a match
308// is not found, NULL is returned.
309const wallet::Address* FindDuplicateAddress(
310    const std::vector<wallet::Address*>& addresses,
311    const wallet::Address& address) {
312  for (size_t i = 0; i < addresses.size(); ++i) {
313    if (addresses[i]->EqualsIgnoreID(address))
314      return addresses[i];
315  }
316  return NULL;
317}
318
319bool IsCardHolderNameValidForWallet(const string16& name) {
320  base::string16 whitespace_collapsed_name = CollapseWhitespace(name, true);
321  std::vector<base::string16> split_name;
322  base::SplitString(whitespace_collapsed_name, ' ', &split_name);
323  return split_name.size() >= 2;
324}
325
326}  // namespace
327
328AutofillDialogController::~AutofillDialogController() {}
329
330AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
331  if (popup_controller_.get())
332    popup_controller_->Hide();
333
334  GetMetricLogger().LogDialogInitialUserState(
335      GetDialogType(), initial_user_state_);
336}
337
338// static
339base::WeakPtr<AutofillDialogControllerImpl>
340    AutofillDialogControllerImpl::Create(
341    content::WebContents* contents,
342    const FormData& form_structure,
343    const GURL& source_url,
344    const DialogType dialog_type,
345    const base::Callback<void(const FormStructure*,
346                              const std::string&)>& callback) {
347  // AutofillDialogControllerImpl owns itself.
348  AutofillDialogControllerImpl* autofill_dialog_controller =
349      new AutofillDialogControllerImpl(contents,
350                                       form_structure,
351                                       source_url,
352                                       dialog_type,
353                                       callback);
354  return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
355}
356
357// static
358void AutofillDialogControllerImpl::RegisterUserPrefs(
359    user_prefs::PrefRegistrySyncable* registry) {
360  registry->RegisterBooleanPref(
361      ::prefs::kAutofillDialogHasPaidWithWallet,
362      false,
363      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
364  registry->RegisterBooleanPref(
365      ::prefs::kAutofillDialogPayWithoutWallet,
366      false,
367      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
368  registry->RegisterDictionaryPref(
369      ::prefs::kAutofillDialogAutofillDefault,
370      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
371}
372
373void AutofillDialogControllerImpl::Show() {
374  dialog_shown_timestamp_ = base::Time::Now();
375
376  content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
377  const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
378  invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
379
380  // Log any relevant UI metrics and security exceptions.
381  GetMetricLogger().LogDialogUiEvent(
382      GetDialogType(), AutofillMetrics::DIALOG_UI_SHOWN);
383
384  GetMetricLogger().LogDialogSecurityMetric(
385      GetDialogType(), AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
386
387  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
388    GetMetricLogger().LogDialogSecurityMetric(
389        GetDialogType(),
390        AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
391  }
392
393  if (!invoked_from_same_origin_) {
394    GetMetricLogger().LogDialogSecurityMetric(
395        GetDialogType(),
396        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
397  }
398
399  // Determine what field types should be included in the dialog.
400  bool has_types = false;
401  bool has_sections = false;
402  form_structure_.ParseFieldTypesFromAutocompleteAttributes(&has_types,
403                                                            &has_sections);
404  // Fail if the author didn't specify autocomplete types.
405  if (!has_types) {
406    callback_.Run(NULL, std::string());
407    delete this;
408    return;
409  }
410
411  const DetailInput kEmailInputs[] = {
412    { 1, EMAIL_ADDRESS, IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL },
413  };
414
415  const DetailInput kCCInputs[] = {
416    { 2, CREDIT_CARD_NUMBER, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER },
417    { 3, CREDIT_CARD_EXP_MONTH },
418    { 3, CREDIT_CARD_EXP_4_DIGIT_YEAR },
419    { 3, CREDIT_CARD_VERIFICATION_CODE, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC },
420    { 4, CREDIT_CARD_NAME, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARDHOLDER_NAME },
421  };
422
423  const DetailInput kBillingInputs[] = {
424    { 5, ADDRESS_BILLING_LINE1,
425      IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_1 },
426    { 6, ADDRESS_BILLING_LINE2,
427      IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_2 },
428    { 7, ADDRESS_BILLING_CITY,
429      IDS_AUTOFILL_DIALOG_PLACEHOLDER_LOCALITY },
430    // TODO(estade): state placeholder should depend on locale.
431    { 8, ADDRESS_BILLING_STATE, IDS_AUTOFILL_FIELD_LABEL_STATE },
432    { 8, ADDRESS_BILLING_ZIP,
433      IDS_AUTOFILL_DIALOG_PLACEHOLDER_POSTAL_CODE, 0.5 },
434    // TODO(estade): this should have a default based on the locale.
435    { 9, ADDRESS_BILLING_COUNTRY, 0 },
436    { 10, PHONE_BILLING_WHOLE_NUMBER,
437      IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER },
438  };
439
440  const DetailInput kShippingInputs[] = {
441    { 11, NAME_FULL, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESSEE_NAME },
442    { 12, ADDRESS_HOME_LINE1, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_1 },
443    { 13, ADDRESS_HOME_LINE2, IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_2 },
444    { 14, ADDRESS_HOME_CITY, IDS_AUTOFILL_DIALOG_PLACEHOLDER_LOCALITY },
445    { 15, ADDRESS_HOME_STATE, IDS_AUTOFILL_FIELD_LABEL_STATE },
446    { 15, ADDRESS_HOME_ZIP, IDS_AUTOFILL_DIALOG_PLACEHOLDER_POSTAL_CODE, 0.5 },
447    { 16, ADDRESS_HOME_COUNTRY, 0 },
448    { 17, PHONE_HOME_WHOLE_NUMBER,
449      IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER },
450  };
451
452  BuildInputs(kEmailInputs,
453              arraysize(kEmailInputs),
454              &requested_email_fields_);
455
456  BuildInputs(kCCInputs,
457              arraysize(kCCInputs),
458              &requested_cc_fields_);
459
460  BuildInputs(kBillingInputs,
461              arraysize(kBillingInputs),
462              &requested_billing_fields_);
463
464  BuildInputs(kCCInputs,
465              arraysize(kCCInputs),
466              &requested_cc_billing_fields_);
467  BuildInputs(kBillingInputs,
468              arraysize(kBillingInputs),
469              &requested_cc_billing_fields_);
470
471  BuildInputs(kShippingInputs,
472              arraysize(kShippingInputs),
473              &requested_shipping_fields_);
474
475  // Test whether we need to show the shipping section. If filling that section
476  // would be a no-op, don't show it.
477  const DetailInputs& inputs = RequestedFieldsForSection(SECTION_SHIPPING);
478  EmptyDataModelWrapper empty_wrapper;
479  cares_about_shipping_ = empty_wrapper.FillFormStructure(
480      inputs,
481      base::Bind(DetailInputMatchesField),
482      &form_structure_);
483
484  SuggestionsUpdated();
485
486  // TODO(estade): don't show the dialog if the site didn't specify the right
487  // fields. First we must figure out what the "right" fields are.
488  view_.reset(CreateView());
489  view_->Show();
490  GetManager()->AddObserver(this);
491
492  // Try to see if the user is already signed-in. If signed-in, fetch the user's
493  // Wallet data. Otherwise, see if the user could be signed in passively.
494  // TODO(aruslan): UMA metrics for sign-in.
495  GetWalletItems();
496
497  if (!account_chooser_model_.WalletIsSelected())
498   LogDialogLatencyToShow();
499}
500
501void AutofillDialogControllerImpl::Hide() {
502  if (view_)
503    view_->Hide();
504}
505
506void AutofillDialogControllerImpl::UpdateProgressBar(double value) {
507  view_->UpdateProgressBar(value);
508}
509
510bool AutofillDialogControllerImpl::AutocheckoutIsRunning() const {
511  return autocheckout_state_ == AUTOCHECKOUT_IN_PROGRESS;
512}
513
514void AutofillDialogControllerImpl::OnAutocheckoutError() {
515  DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
516  GetMetricLogger().LogAutocheckoutDuration(
517      base::Time::Now() - autocheckout_started_timestamp_,
518      AutofillMetrics::AUTOCHECKOUT_FAILED);
519  autocheckout_state_ = AUTOCHECKOUT_ERROR;
520  autocheckout_started_timestamp_ = base::Time();
521  view_->UpdateNotificationArea();
522  view_->UpdateButtonStrip();
523  view_->UpdateDetailArea();
524}
525
526void AutofillDialogControllerImpl::OnAutocheckoutSuccess() {
527  DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
528  GetMetricLogger().LogAutocheckoutDuration(
529      base::Time::Now() - autocheckout_started_timestamp_,
530      AutofillMetrics::AUTOCHECKOUT_SUCCEEDED);
531  autocheckout_state_ = AUTOCHECKOUT_SUCCESS;
532  autocheckout_started_timestamp_ = base::Time();
533  view_->UpdateNotificationArea();
534  view_->UpdateButtonStrip();
535}
536
537////////////////////////////////////////////////////////////////////////////////
538// AutofillDialogController implementation.
539
540string16 AutofillDialogControllerImpl::DialogTitle() const {
541  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE);
542}
543
544string16 AutofillDialogControllerImpl::EditSuggestionText() const {
545  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT);
546}
547
548string16 AutofillDialogControllerImpl::CancelButtonText() const {
549  if (autocheckout_state_ == AUTOCHECKOUT_ERROR)
550    return l10n_util::GetStringUTF16(IDS_OK);
551  if (autocheckout_state_ == AUTOCHECKOUT_SUCCESS)
552    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_CONTINUE_BUTTON);
553  return l10n_util::GetStringUTF16(IDS_CANCEL);
554}
555
556string16 AutofillDialogControllerImpl::ConfirmButtonText() const {
557  return l10n_util::GetStringUTF16(IsSubmitPausedOn(wallet::VERIFY_CVV) ?
558      IDS_AUTOFILL_DIALOG_VERIFY_BUTTON : IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON);
559}
560
561string16 AutofillDialogControllerImpl::SaveLocallyText() const {
562  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX);
563}
564
565string16 AutofillDialogControllerImpl::ProgressBarText() const {
566  return l10n_util::GetStringUTF16(
567      IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_PROGRESS_BAR);
568}
569
570string16 AutofillDialogControllerImpl::LegalDocumentsText() {
571  if (!IsPayingWithWallet())
572    return string16();
573
574  EnsureLegalDocumentsText();
575  return legal_documents_text_;
576}
577
578DialogSignedInState AutofillDialogControllerImpl::SignedInState() const {
579  if (account_chooser_model_.had_wallet_error())
580    return SIGN_IN_DISABLED;
581
582  if (signin_helper_ || !wallet_items_)
583    return REQUIRES_RESPONSE;
584
585  if (wallet_items_->HasRequiredAction(wallet::GAIA_AUTH))
586    return REQUIRES_SIGN_IN;
587
588  if (wallet_items_->HasRequiredAction(wallet::PASSIVE_GAIA_AUTH))
589    return REQUIRES_PASSIVE_SIGN_IN;
590
591  return SIGNED_IN;
592}
593
594bool AutofillDialogControllerImpl::ShouldShowSpinner() const {
595  return account_chooser_model_.WalletIsSelected() &&
596         SignedInState() == REQUIRES_RESPONSE;
597}
598
599string16 AutofillDialogControllerImpl::AccountChooserText() const {
600  // TODO(aruslan): this should be l10n "Not using Google Wallet".
601  if (!account_chooser_model_.WalletIsSelected())
602    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PAY_WITHOUT_WALLET);
603
604  if (SignedInState() == SIGNED_IN)
605    return account_chooser_model_.active_wallet_account_name();
606
607  // In this case, the account chooser should be showing the signin link.
608  return string16();
609}
610
611string16 AutofillDialogControllerImpl::SignInLinkText() const {
612  return l10n_util::GetStringUTF16(
613      signin_registrar_.IsEmpty() ? IDS_AUTOFILL_DIALOG_SIGN_IN :
614                                    IDS_AUTOFILL_DIALOG_PAY_WITHOUT_WALLET);
615}
616
617bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
618  // If Autocheckout is running, hide this checkbox so the progress bar has some
619  // room. If Autocheckout had an error, neither the [X] Save details in chrome
620  // nor the progress bar should show.
621  return !IsPayingWithWallet() &&
622      !profile_->IsOffTheRecord() &&
623      IsManuallyEditingAnySection() &&
624      !ShouldShowProgressBar() &&
625      autocheckout_state_ != AUTOCHECKOUT_ERROR;
626}
627
628int AutofillDialogControllerImpl::GetDialogButtons() const {
629  if (autocheckout_state_ != AUTOCHECKOUT_NOT_STARTED)
630    return ui::DIALOG_BUTTON_CANCEL;
631  return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
632}
633
634bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
635    ui::DialogButton button) const {
636  if (button == ui::DIALOG_BUTTON_OK) {
637    if (IsSubmitPausedOn(wallet::VERIFY_CVV))
638      return true;
639    if (is_submitting_ || ShouldShowSpinner())
640      return false;
641    return true;
642  }
643
644  DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button);
645  // TODO(ahutter): Make it possible for the user to cancel out of the dialog
646  // while Autocheckout is in progress.
647  return autocheckout_state_ != AUTOCHECKOUT_IN_PROGRESS ||
648         !callback_.is_null();
649}
650
651const std::vector<ui::Range>& AutofillDialogControllerImpl::
652    LegalDocumentLinks() {
653  EnsureLegalDocumentsText();
654  return legal_document_link_ranges_;
655}
656
657bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section)
658    const {
659  if (IsSubmitPausedOn(wallet::VERIFY_CVV))
660    return section == SECTION_CC_BILLING;
661
662  if (!FormStructureCaresAboutSection(section))
663    return false;
664
665  if (IsPayingWithWallet())
666    return section == SECTION_CC_BILLING || section == SECTION_SHIPPING;
667
668  return section != SECTION_CC_BILLING;
669}
670
671bool AutofillDialogControllerImpl::HasCompleteWallet() const {
672  return wallet_items_.get() != NULL &&
673         !wallet_items_->instruments().empty() &&
674         !wallet_items_->addresses().empty();
675}
676
677bool AutofillDialogControllerImpl::IsSubmitPausedOn(
678    wallet::RequiredAction required_action) const {
679  return full_wallet_ && full_wallet_->HasRequiredAction(required_action);
680}
681
682void AutofillDialogControllerImpl::GetWalletItems() {
683  GetWalletClient()->GetWalletItems(source_url_);
684}
685
686void AutofillDialogControllerImpl::HideSignIn() {
687  signin_registrar_.RemoveAll();
688  view_->HideSignIn();
689  view_->UpdateAccountChooser();
690}
691
692void AutofillDialogControllerImpl::SignedInStateUpdated() {
693  switch (SignedInState()) {
694    case SIGNED_IN:
695      // Start fetching the user name if we don't know it yet.
696      if (account_chooser_model_.active_wallet_account_name().empty()) {
697        signin_helper_.reset(new wallet::WalletSigninHelper(
698            this, profile_->GetRequestContext()));
699        signin_helper_->StartUserNameFetch();
700      } else {
701        LogDialogLatencyToShow();
702      }
703      break;
704
705    case REQUIRES_SIGN_IN:
706    case SIGN_IN_DISABLED:
707      // Switch to the local account and refresh the dialog.
708      OnWalletSigninError();
709      break;
710
711    case REQUIRES_PASSIVE_SIGN_IN:
712      // Attempt to passively sign in the user.
713      DCHECK(!signin_helper_);
714      account_chooser_model_.ClearActiveWalletAccountName();
715      signin_helper_.reset(new wallet::WalletSigninHelper(
716          this,
717          profile_->GetRequestContext()));
718      signin_helper_->StartPassiveSignin();
719      break;
720
721    case REQUIRES_RESPONSE:
722      break;
723  }
724}
725
726void AutofillDialogControllerImpl::OnWalletOrSigninUpdate() {
727  SignedInStateUpdated();
728  SuggestionsUpdated();
729  UpdateAccountChooserView();
730
731  if (view_)
732    view_->UpdateButtonStrip();
733
734  // On the first successful response, compute the initial user state metric.
735  if (initial_user_state_ == AutofillMetrics::DIALOG_USER_STATE_UNKNOWN)
736    initial_user_state_ = GetInitialUserState();
737}
738
739void AutofillDialogControllerImpl::EnsureLegalDocumentsText() {
740  if (!wallet_items_ || wallet_items_->legal_documents().empty())
741    return;
742
743  // The text has already been constructed, no need to recompute.
744  if (!legal_documents_text_.empty())
745    return;
746
747  const std::vector<wallet::WalletItems::LegalDocument*>& documents =
748      wallet_items_->legal_documents();
749  DCHECK_LE(documents.size(), 3U);
750  DCHECK_GE(documents.size(), 2U);
751  const bool new_user = wallet_items_->HasRequiredAction(wallet::SETUP_WALLET);
752
753  const string16 privacy_policy_display_name =
754      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK);
755  string16 text;
756  if (documents.size() == 2U) {
757    text = l10n_util::GetStringFUTF16(
758        new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_2 :
759                   IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_2,
760        documents[0]->display_name(),
761        documents[1]->display_name());
762  } else {
763    text = l10n_util::GetStringFUTF16(
764        new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_3 :
765                   IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_3,
766        documents[0]->display_name(),
767        documents[1]->display_name(),
768        documents[2]->display_name());
769  }
770
771  legal_document_link_ranges_.clear();
772  for (size_t i = 0; i < documents.size(); ++i) {
773    size_t link_start = text.find(documents[i]->display_name());
774    legal_document_link_ranges_.push_back(ui::Range(
775        link_start, link_start + documents[i]->display_name().size()));
776  }
777  legal_documents_text_ = text;
778}
779
780void AutofillDialogControllerImpl::PrepareDetailInputsForSection(
781    DialogSection section) {
782  SetEditingExistingData(section, false);
783  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
784
785  // If the chosen item in |model| yields an empty suggestion text, it is
786  // invalid. In this case, show the editing UI with invalid fields highlighted.
787  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
788  if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) &&
789      SuggestionTextForSection(section).empty()) {
790    SetEditingExistingData(section, true);
791  }
792
793  // Reset all previously entered data and stop editing |section|.
794  DetailInputs* inputs = MutableRequestedFieldsForSection(section);
795  for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
796    it->initial_value.clear();
797    it->editable = InputIsEditable(*it, section);
798  }
799
800  if (wrapper && IsEditingExistingData(section))
801    wrapper->FillInputs(inputs);
802
803  if (view_)
804    view_->UpdateSection(section);
805}
806
807const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection(
808    DialogSection section) const {
809  switch (section) {
810    case SECTION_EMAIL:
811      return requested_email_fields_;
812    case SECTION_CC:
813      return requested_cc_fields_;
814    case SECTION_BILLING:
815      return requested_billing_fields_;
816    case SECTION_CC_BILLING:
817      return requested_cc_billing_fields_;
818    case SECTION_SHIPPING:
819      return requested_shipping_fields_;
820  }
821
822  NOTREACHED();
823  return requested_billing_fields_;
824}
825
826ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
827    AutofillFieldType type) {
828  switch (AutofillType::GetEquivalentFieldType(type)) {
829    case CREDIT_CARD_EXP_MONTH:
830      return &cc_exp_month_combobox_model_;
831
832    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
833      return &cc_exp_year_combobox_model_;
834
835    case ADDRESS_HOME_COUNTRY:
836      return &country_combobox_model_;
837
838    default:
839      return NULL;
840  }
841}
842
843ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSection(
844    DialogSection section) {
845  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
846  // The shipping section menu is special. It will always show because there is
847  // a choice between "Use billing" and "enter new".
848  if (section == SECTION_SHIPPING)
849    return model;
850
851  // For other sections, only show a menu if there's at least one suggestion.
852  for (int i = 0; i < model->GetItemCount(); ++i) {
853    if (IsASuggestionItemKey(model->GetItemKeyAt(i)))
854      return model;
855  }
856
857  return NULL;
858}
859
860#if defined(OS_ANDROID)
861ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSectionHack(
862    DialogSection section) {
863  return SuggestionsMenuModelForSection(section);
864}
865#endif
866
867ui::MenuModel* AutofillDialogControllerImpl::MenuModelForAccountChooser() {
868  // If there were unrecoverable Wallet errors, or if there are choices other
869  // than "Pay without the wallet", show the full menu.
870  if (account_chooser_model_.had_wallet_error() ||
871      account_chooser_model_.HasAccountsToChoose()) {
872    return &account_chooser_model_;
873  }
874
875  // Otherwise, there is no menu, just a sign in link.
876  return NULL;
877}
878
879gfx::Image AutofillDialogControllerImpl::AccountChooserImage() {
880  if (!MenuModelForAccountChooser()) {
881    if (signin_registrar_.IsEmpty()) {
882      return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
883          IDR_WALLET_ICON);
884    }
885
886    return gfx::Image();
887  }
888
889  gfx::Image icon;
890  account_chooser_model_.GetIconAt(
891      account_chooser_model_.GetIndexOfCommandId(
892          account_chooser_model_.checked_item()),
893      &icon);
894  return icon;
895}
896
897bool AutofillDialogControllerImpl::ShouldShowDetailArea() const {
898  // Hide the detail area when Autocheckout is running or there was an error (as
899  // there's nothing they can do after an error but cancel).
900  return autocheckout_state_ == AUTOCHECKOUT_NOT_STARTED;
901}
902
903bool AutofillDialogControllerImpl::ShouldShowProgressBar() const {
904  // Show the progress bar while Autocheckout is running but hide it on errors,
905  // as there's no use leaving it up if the flow has failed.
906  return autocheckout_state_ == AUTOCHECKOUT_IN_PROGRESS;
907}
908
909string16 AutofillDialogControllerImpl::LabelForSection(DialogSection section)
910    const {
911  switch (section) {
912    case SECTION_EMAIL:
913      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_EMAIL);
914    case SECTION_CC:
915      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC);
916    case SECTION_BILLING:
917      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING);
918    case SECTION_CC_BILLING:
919      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC_BILLING);
920    case SECTION_SHIPPING:
921      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING);
922    default:
923      NOTREACHED();
924      return string16();
925  }
926}
927
928SuggestionState AutofillDialogControllerImpl::SuggestionStateForSection(
929    DialogSection section) {
930  return SuggestionState(SuggestionTextForSection(section),
931                         SuggestionTextStyleForSection(section),
932                         SuggestionIconForSection(section),
933                         ExtraSuggestionTextForSection(section),
934                         ExtraSuggestionIconForSection(section),
935                         EditEnabledForSection(section));
936}
937
938string16 AutofillDialogControllerImpl::SuggestionTextForSection(
939    DialogSection section) {
940  string16 action_text = RequiredActionTextForSection(section);
941  if (!action_text.empty())
942    return action_text;
943
944  // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a
945  // user selects a credit card that has expired), don't show a suggestion (even
946  // though there is a profile selected in the model).
947  if (IsEditingExistingData(section))
948    return string16();
949
950  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
951  std::string item_key = model->GetItemKeyForCheckedItem();
952  if (item_key == kSameAsBillingKey) {
953    return l10n_util::GetStringUTF16(
954        IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING);
955  }
956
957  if (!IsASuggestionItemKey(item_key))
958    return string16();
959
960  if (section == SECTION_EMAIL)
961    return model->GetLabelAt(model->checked_item());
962
963  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
964  return wrapper->GetDisplayText();
965}
966
967gfx::Font::FontStyle
968    AutofillDialogControllerImpl::SuggestionTextStyleForSection(
969        DialogSection section) const {
970  const SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
971  if (model->GetItemKeyForCheckedItem() == kSameAsBillingKey)
972    return gfx::Font::ITALIC;
973
974  return gfx::Font::NORMAL;
975}
976
977string16 AutofillDialogControllerImpl::RequiredActionTextForSection(
978    DialogSection section) const {
979  if (section == SECTION_CC_BILLING && IsSubmitPausedOn(wallet::VERIFY_CVV)) {
980    const wallet::WalletItems::MaskedInstrument* current_instrument =
981        wallet_items_->GetInstrumentById(active_instrument_id_);
982    if (current_instrument)
983      return current_instrument->TypeAndLastFourDigits();
984
985    DetailOutputMap output;
986    view_->GetUserInput(section, &output);
987    CreditCard card;
988    GetBillingInfoFromOutputs(output, &card, NULL, NULL);
989    return card.TypeAndLastFourDigits();
990  }
991
992  return string16();
993}
994
995string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
996    DialogSection section) const {
997  if (section == SECTION_CC ||
998      (section == SECTION_CC_BILLING && IsSubmitPausedOn(wallet::VERIFY_CVV))) {
999    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC);
1000  }
1001
1002  return string16();
1003}
1004
1005const wallet::WalletItems::MaskedInstrument* AutofillDialogControllerImpl::
1006    ActiveInstrument() const {
1007  if (!IsPayingWithWallet())
1008    return NULL;
1009
1010  const SuggestionsMenuModel* model =
1011      SuggestionsMenuModelForSection(SECTION_CC_BILLING);
1012  const std::string item_key = model->GetItemKeyForCheckedItem();
1013  if (!IsASuggestionItemKey(item_key))
1014    return NULL;
1015
1016  int index;
1017  if (!base::StringToInt(item_key, &index) || index < 0 ||
1018      static_cast<size_t>(index) >= wallet_items_->instruments().size()) {
1019    NOTREACHED();
1020    return NULL;
1021  }
1022
1023  return wallet_items_->instruments()[index];
1024}
1025
1026const wallet::Address* AutofillDialogControllerImpl::
1027    ActiveShippingAddress() const {
1028  if (!IsPayingWithWallet())
1029    return NULL;
1030
1031  const SuggestionsMenuModel* model =
1032      SuggestionsMenuModelForSection(SECTION_SHIPPING);
1033  const std::string item_key = model->GetItemKeyForCheckedItem();
1034  if (!IsASuggestionItemKey(item_key))
1035    return NULL;
1036
1037  int index;
1038  if (!base::StringToInt(item_key, &index) || index < 0 ||
1039      static_cast<size_t>(index) >= wallet_items_->addresses().size()) {
1040    NOTREACHED();
1041    return NULL;
1042  }
1043
1044  return wallet_items_->addresses()[index];
1045}
1046
1047scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
1048    DialogSection section) {
1049  if (IsPayingWithWallet() && full_wallet_ &&
1050      full_wallet_->required_actions().empty()) {
1051    if (section == SECTION_CC_BILLING) {
1052      return scoped_ptr<DataModelWrapper>(
1053          new FullWalletBillingWrapper(full_wallet_.get()));
1054    }
1055    if (section == SECTION_SHIPPING) {
1056      return scoped_ptr<DataModelWrapper>(
1057          new FullWalletShippingWrapper(full_wallet_.get()));
1058    }
1059  }
1060
1061  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1062  std::string item_key = model->GetItemKeyForCheckedItem();
1063  if (!IsASuggestionItemKey(item_key) || IsManuallyEditingSection(section))
1064    return scoped_ptr<DataModelWrapper>();
1065
1066  if (IsPayingWithWallet()) {
1067    if (section == SECTION_CC_BILLING) {
1068      return scoped_ptr<DataModelWrapper>(
1069          new WalletInstrumentWrapper(ActiveInstrument()));
1070    }
1071
1072    if (section == SECTION_SHIPPING) {
1073      return scoped_ptr<DataModelWrapper>(
1074          new WalletAddressWrapper(ActiveShippingAddress()));
1075    }
1076
1077    return scoped_ptr<DataModelWrapper>();
1078  }
1079
1080  if (section == SECTION_CC) {
1081    CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
1082    DCHECK(card);
1083    return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card));
1084  }
1085
1086  AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
1087  DCHECK(profile);
1088  size_t variant = GetSelectedVariantForModel(*model);
1089  return scoped_ptr<DataModelWrapper>(
1090      new AutofillProfileWrapper(profile, variant));
1091}
1092
1093gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
1094    DialogSection section) {
1095  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
1096  if (!model.get())
1097    return gfx::Image();
1098
1099  return model->GetIcon();
1100}
1101
1102gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
1103    DialogSection section) const {
1104  if (section == SECTION_CC || section == SECTION_CC_BILLING)
1105    return IconForField(CREDIT_CARD_VERIFICATION_CODE, string16());
1106
1107  return gfx::Image();
1108}
1109
1110bool AutofillDialogControllerImpl::EditEnabledForSection(
1111    DialogSection section) const {
1112  if (SuggestionsMenuModelForSection(section)->GetItemKeyForCheckedItem() ==
1113      kSameAsBillingKey) {
1114    return false;
1115  }
1116
1117  if (section == SECTION_CC_BILLING && IsSubmitPausedOn(wallet::VERIFY_CVV))
1118    return false;
1119
1120  return true;
1121}
1122
1123void AutofillDialogControllerImpl::EditClickedForSection(
1124    DialogSection section) {
1125  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
1126  SetEditingExistingData(section, true);
1127
1128  DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1129  for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
1130    it->editable = InputIsEditable(*it, section);
1131  }
1132  model->FillInputs(inputs);
1133
1134  view_->UpdateSection(section);
1135
1136  GetMetricLogger().LogDialogUiEvent(
1137      GetDialogType(), DialogSectionToUiEditEvent(section));
1138}
1139
1140void AutofillDialogControllerImpl::EditCancelledForSection(
1141    DialogSection section) {
1142  PrepareDetailInputsForSection(section);
1143}
1144
1145gfx::Image AutofillDialogControllerImpl::IconForField(
1146    AutofillFieldType type, const string16& user_input) const {
1147  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1148  if (type == CREDIT_CARD_VERIFICATION_CODE)
1149    return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT);
1150
1151  // For the credit card, we show a few grayscale images, and possibly one
1152  // color image if |user_input| is a valid card number.
1153  if (type == CREDIT_CARD_NUMBER) {
1154    const int card_idrs[] = {
1155      IDR_AUTOFILL_CC_VISA,
1156      IDR_AUTOFILL_CC_MASTERCARD,
1157      IDR_AUTOFILL_CC_AMEX,
1158      IDR_AUTOFILL_CC_DISCOVER
1159    };
1160    const int number_of_cards = arraysize(card_idrs);
1161    // The number of pixels between card icons.
1162    const int kCardPadding = 2;
1163
1164    gfx::ImageSkia some_card = *rb.GetImageSkiaNamed(card_idrs[0]);
1165    const int card_width = some_card.width();
1166    gfx::Canvas canvas(
1167        gfx::Size((card_width + kCardPadding) * number_of_cards - kCardPadding,
1168                  some_card.height()),
1169        ui::SCALE_FACTOR_100P,
1170        false);
1171    CreditCard card;
1172    card.SetRawInfo(CREDIT_CARD_NUMBER, user_input);
1173
1174    for (int i = 0; i < number_of_cards; ++i) {
1175      int idr = card_idrs[i];
1176      gfx::ImageSkia card_image = *rb.GetImageSkiaNamed(idr);
1177      if (card.IconResourceId() != idr) {
1178        SkBitmap disabled_bitmap =
1179            SkBitmapOperations::CreateHSLShiftedBitmap(*card_image.bitmap(),
1180                                                       kGrayImageShift);
1181        card_image = gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap);
1182      }
1183
1184      canvas.DrawImageInt(card_image, i * (card_width + kCardPadding), 0);
1185    }
1186
1187    gfx::ImageSkia skia(canvas.ExtractImageRep());
1188    return gfx::Image(skia);
1189  }
1190
1191  return gfx::Image();
1192}
1193
1194// TODO(estade): Replace all the error messages here with more helpful and
1195// translateable ones. TODO(groby): Also add tests.
1196string16 AutofillDialogControllerImpl::InputValidityMessage(
1197    AutofillFieldType type,
1198    const string16& value) const {
1199  switch (AutofillType::GetEquivalentFieldType(type)) {
1200    case EMAIL_ADDRESS:
1201      if (!value.empty() && !IsValidEmailAddress(value)) {
1202        return l10n_util::GetStringUTF16(
1203            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS);
1204      }
1205      break;
1206
1207    case CREDIT_CARD_NUMBER:
1208      if (!value.empty() && !autofill::IsValidCreditCardNumber(value)) {
1209        return l10n_util::GetStringUTF16(
1210            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER);
1211      }
1212      break;
1213
1214    case CREDIT_CARD_NAME:
1215      // Wallet requires a first and last name.
1216      if (!value.empty() && IsPayingWithWallet() &&
1217          !IsCardHolderNameValidForWallet(value)) {
1218        return l10n_util::GetStringUTF16(
1219            IDS_AUTOFILL_DIALOG_VALIDATION_WALLET_REQUIRES_TWO_NAMES);
1220      }
1221      break;
1222
1223    case CREDIT_CARD_EXP_MONTH:
1224    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
1225      break;
1226
1227    case CREDIT_CARD_VERIFICATION_CODE:
1228      if (!value.empty() && !autofill::IsValidCreditCardSecurityCode(value)) {
1229        return l10n_util::GetStringUTF16(
1230            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1231      }
1232      break;
1233
1234    case ADDRESS_HOME_LINE1:
1235      break;
1236
1237    case ADDRESS_HOME_LINE2:
1238      return base::string16();  // Line 2 is optional - always valid.
1239
1240    case ADDRESS_HOME_CITY:
1241    case ADDRESS_HOME_STATE:
1242    case ADDRESS_HOME_ZIP:
1243    case ADDRESS_HOME_COUNTRY:
1244      break;
1245
1246    case NAME_FULL:  // Used for shipping.
1247      break;
1248
1249    case PHONE_HOME_WHOLE_NUMBER:  // Used in shipping section.
1250      break;
1251
1252    case PHONE_BILLING_WHOLE_NUMBER:  // Used in billing section.
1253      break;
1254
1255    default:
1256      NOTREACHED();  // Trying to validate unknown field.
1257      break;
1258  }
1259
1260  return value.empty() ?
1261      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE) :
1262      base::string16();
1263}
1264
1265// TODO(estade): Replace all the error messages here with more helpful and
1266// translateable ones. TODO(groby): Also add tests.
1267ValidityData AutofillDialogControllerImpl::InputsAreValid(
1268    const DetailOutputMap& inputs, ValidationType validation_type) const {
1269  ValidityData invalid_messages;
1270  std::map<AutofillFieldType, string16> field_values;
1271  for (DetailOutputMap::const_iterator iter = inputs.begin();
1272       iter != inputs.end(); ++iter) {
1273    // Skip empty fields in edit mode.
1274    if (validation_type == VALIDATE_EDIT && iter->second.empty())
1275      continue;
1276
1277    const AutofillFieldType type = iter->first->type;
1278    string16 message = InputValidityMessage(type, iter->second);
1279    if (!message.empty())
1280      invalid_messages[type] = message;
1281    else
1282      field_values[type] = iter->second;
1283  }
1284
1285  // Validate the date formed by month and year field. (Autofill dialog is
1286  // never supposed to have 2-digit years, so not checked).
1287  if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
1288      field_values.count(CREDIT_CARD_EXP_MONTH) &&
1289      !IsCreditCardExpirationValid(field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
1290                                   field_values[CREDIT_CARD_EXP_MONTH])) {
1291    // The dialog shows the same error message for the month and year fields.
1292    invalid_messages[CREDIT_CARD_EXP_4_DIGIT_YEAR] = l10n_util::GetStringUTF16(
1293        IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1294    invalid_messages[CREDIT_CARD_EXP_MONTH] = l10n_util::GetStringUTF16(
1295        IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1296  }
1297
1298  // If there is a credit card number and a CVC, validate them together.
1299  if (field_values.count(CREDIT_CARD_NUMBER) &&
1300      field_values.count(CREDIT_CARD_VERIFICATION_CODE) &&
1301      InputValidityMessage(CREDIT_CARD_NUMBER,
1302                           field_values[CREDIT_CARD_NUMBER]).empty() &&
1303      !autofill::IsValidCreditCardSecurityCode(
1304          field_values[CREDIT_CARD_VERIFICATION_CODE],
1305          field_values[CREDIT_CARD_NUMBER])) {
1306    invalid_messages[CREDIT_CARD_VERIFICATION_CODE] =
1307        l10n_util::GetStringUTF16(
1308            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1309  }
1310
1311  // Validate the shipping phone number against the country code of the address.
1312  if (field_values.count(ADDRESS_HOME_COUNTRY) &&
1313      field_values.count(PHONE_HOME_WHOLE_NUMBER)) {
1314    i18n::PhoneObject phone_object(
1315        field_values[PHONE_HOME_WHOLE_NUMBER],
1316        AutofillCountry::GetCountryCode(
1317            field_values[ADDRESS_HOME_COUNTRY],
1318            g_browser_process->GetApplicationLocale()));
1319    if (!phone_object.IsValidNumber()) {
1320      invalid_messages[PHONE_HOME_WHOLE_NUMBER] = l10n_util::GetStringUTF16(
1321          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1322    }
1323  }
1324
1325  // Validate the billing phone number against the country code of the address.
1326  if (field_values.count(ADDRESS_BILLING_COUNTRY) &&
1327      field_values.count(PHONE_BILLING_WHOLE_NUMBER)) {
1328    i18n::PhoneObject phone_object(
1329        field_values[PHONE_BILLING_WHOLE_NUMBER],
1330        AutofillCountry::GetCountryCode(
1331            field_values[ADDRESS_BILLING_COUNTRY],
1332            g_browser_process->GetApplicationLocale()));
1333    if (!phone_object.IsValidNumber()) {
1334      invalid_messages[PHONE_BILLING_WHOLE_NUMBER] = l10n_util::GetStringUTF16(
1335          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1336    }
1337  }
1338
1339  return invalid_messages;
1340}
1341
1342void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1343    const DetailInput* input,
1344    gfx::NativeView parent_view,
1345    const gfx::Rect& content_bounds,
1346    const string16& field_contents,
1347    bool was_edit) {
1348  // If the field is edited down to empty, don't show a popup.
1349  if (was_edit && field_contents.empty()) {
1350    HidePopup();
1351    return;
1352  }
1353
1354  // If the user clicks while the popup is already showing, be sure to hide
1355  // it.
1356  if (!was_edit && popup_controller_.get()) {
1357    HidePopup();
1358    return;
1359  }
1360
1361  std::vector<string16> popup_values, popup_labels, popup_icons;
1362  if (IsCreditCardType(input->type)) {
1363    GetManager()->GetCreditCardSuggestions(input->type,
1364                                           field_contents,
1365                                           &popup_values,
1366                                           &popup_labels,
1367                                           &popup_icons,
1368                                           &popup_guids_);
1369  } else {
1370    std::vector<AutofillFieldType> field_types;
1371    field_types.push_back(EMAIL_ADDRESS);
1372    for (DetailInputs::const_iterator iter = requested_shipping_fields_.begin();
1373         iter != requested_shipping_fields_.end(); ++iter) {
1374      field_types.push_back(iter->type);
1375    }
1376    GetManager()->GetProfileSuggestions(input->type,
1377                                        field_contents,
1378                                        false,
1379                                        field_types,
1380                                        &popup_values,
1381                                        &popup_labels,
1382                                        &popup_icons,
1383                                        &popup_guids_);
1384  }
1385
1386  if (popup_values.empty()) {
1387    HidePopup();
1388    return;
1389  }
1390
1391  // TODO(estade): do we need separators and control rows like 'Clear
1392  // Form'?
1393  std::vector<int> popup_ids;
1394  for (size_t i = 0; i < popup_guids_.size(); ++i) {
1395    popup_ids.push_back(i);
1396  }
1397
1398  popup_controller_ = AutofillPopupControllerImpl::GetOrCreate(
1399      popup_controller_,
1400      weak_ptr_factory_.GetWeakPtr(),
1401      parent_view,
1402      content_bounds);
1403  popup_controller_->Show(popup_values,
1404                          popup_labels,
1405                          popup_icons,
1406                          popup_ids);
1407  input_showing_popup_ = input;
1408}
1409
1410void AutofillDialogControllerImpl::FocusMoved() {
1411  HidePopup();
1412}
1413
1414void AutofillDialogControllerImpl::ViewClosed() {
1415  GetManager()->RemoveObserver(this);
1416
1417  // TODO(ahutter): Once a user can cancel Autocheckout mid-flow, log that
1418  // metric here.
1419
1420  delete this;
1421}
1422
1423std::vector<DialogNotification> AutofillDialogControllerImpl::
1424    CurrentNotifications() {
1425  std::vector<DialogNotification> notifications;
1426
1427  if (account_chooser_model_.had_wallet_error()) {
1428    // TODO(dbeam): pass along the Wallet error or remove from the translation.
1429    // TODO(dbeam): figure out a way to dismiss this error after a while.
1430    notifications.push_back(DialogNotification(
1431        DialogNotification::WALLET_ERROR,
1432        l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_COMPLETE_WITHOUT_WALLET,
1433                                   ASCIIToUTF16("[Wallet-Error]."))));
1434  } else if (should_show_wallet_promo_) {
1435    if (IsPayingWithWallet() && HasCompleteWallet()) {
1436      notifications.push_back(DialogNotification(
1437          DialogNotification::EXPLANATORY_MESSAGE,
1438          l10n_util::GetStringUTF16(
1439              IDS_AUTOFILL_DIALOG_DETAILS_FROM_WALLET)));
1440    } else if ((IsPayingWithWallet() && !HasCompleteWallet()) ||
1441               has_shown_wallet_usage_confirmation_) {
1442      DialogNotification notification(
1443          DialogNotification::WALLET_USAGE_CONFIRMATION,
1444          l10n_util::GetStringUTF16(
1445              IDS_AUTOFILL_DIALOG_SAVE_DETAILS_IN_WALLET));
1446      notification.set_checked(account_chooser_model_.WalletIsSelected());
1447      notification.set_interactive(!is_submitting_);
1448      notifications.push_back(notification);
1449      has_shown_wallet_usage_confirmation_ = true;
1450    }
1451  }
1452
1453  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
1454    notifications.push_back(DialogNotification(
1455        DialogNotification::SECURITY_WARNING,
1456        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECURITY_WARNING)));
1457  }
1458
1459  if (!invoked_from_same_origin_) {
1460    notifications.push_back(DialogNotification(
1461        DialogNotification::SECURITY_WARNING,
1462        l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING,
1463                                   UTF8ToUTF16(source_url_.host()))));
1464  }
1465
1466  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1467    notifications.push_back(DialogNotification(
1468        DialogNotification::REQUIRED_ACTION,
1469        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VERIFY_CVV)));
1470  }
1471
1472  if (autocheckout_state_ == AUTOCHECKOUT_ERROR) {
1473    notifications.push_back(DialogNotification(
1474        DialogNotification::AUTOCHECKOUT_ERROR,
1475        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_ERROR)));
1476  }
1477
1478  if (autocheckout_state_ == AUTOCHECKOUT_SUCCESS) {
1479    notifications.push_back(DialogNotification(
1480        DialogNotification::AUTOCHECKOUT_SUCCESS,
1481        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_SUCCESS)));
1482  }
1483
1484  if (wallet_server_validation_error_) {
1485    // TODO(ahutter): L10n and UI.
1486    notifications.push_back(DialogNotification(
1487        DialogNotification::REQUIRED_ACTION,
1488        ASCIIToUTF16("New data failed validation on server side")));
1489  }
1490
1491  if (IsPayingWithWallet() && !wallet::IsUsingProd()) {
1492    notifications.push_back(DialogNotification(
1493        DialogNotification::DEVELOPER_WARNING,
1494        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_NOT_PROD_WARNING)));
1495  }
1496
1497  if (choose_another_instrument_or_address_) {
1498    notifications.push_back(DialogNotification(
1499        DialogNotification::REQUIRED_ACTION,
1500        ASCIIToUTF16("We need more information to complete your purchase.")));
1501  }
1502
1503  return notifications;
1504}
1505
1506void AutofillDialogControllerImpl::SignInLinkClicked() {
1507  if (signin_registrar_.IsEmpty()) {
1508    // Start sign in.
1509    DCHECK(!IsPayingWithWallet());
1510
1511    content::Source<content::NavigationController> source(view_->ShowSignIn());
1512    signin_registrar_.Add(
1513        this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
1514    view_->UpdateAccountChooser();
1515
1516    GetMetricLogger().LogDialogUiEvent(
1517        GetDialogType(), AutofillMetrics::DIALOG_UI_SIGNIN_SHOWN);
1518  } else {
1519    HideSignIn();
1520  }
1521}
1522
1523void AutofillDialogControllerImpl::NotificationCheckboxStateChanged(
1524    DialogNotification::Type type, bool checked) {
1525  if (type == DialogNotification::WALLET_USAGE_CONFIRMATION) {
1526    if (checked)
1527      account_chooser_model_.SelectActiveWalletAccount();
1528    else
1529      account_chooser_model_.SelectUseAutofill();
1530  }
1531}
1532
1533void AutofillDialogControllerImpl::LegalDocumentLinkClicked(
1534    const ui::Range& range) {
1535  for (size_t i = 0; i < legal_document_link_ranges_.size(); ++i) {
1536    if (legal_document_link_ranges_[i] == range) {
1537      OpenTabWithUrl(wallet_items_->legal_documents()[i]->url());
1538      return;
1539    }
1540  }
1541
1542  NOTREACHED();
1543}
1544
1545void AutofillDialogControllerImpl::OnCancel() {
1546  HidePopup();
1547
1548  // If the submit was successful, |callback_| will have already been |.Run()|
1549  // and nullified. If this is the case, no further actions are required. If
1550  // Autocheckout has an error, it's possible that the dialog will be submitted
1551  // to start the flow and then cancelled to close the dialog after the error.
1552  if (callback_.is_null())
1553    return;
1554
1555  LogOnCancelMetrics();
1556
1557  callback_.Run(NULL, std::string());
1558  callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
1559}
1560
1561void AutofillDialogControllerImpl::OnAccept() {
1562  choose_another_instrument_or_address_ = false;
1563  HidePopup();
1564  SetIsSubmitting(true);
1565  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1566    DCHECK(!active_instrument_id_.empty());
1567    GetWalletClient()->AuthenticateInstrument(
1568        active_instrument_id_,
1569        UTF16ToUTF8(view_->GetCvc()),
1570        wallet_items_->obfuscated_gaia_id());
1571  } else if (IsPayingWithWallet()) {
1572    SubmitWithWallet();
1573  } else {
1574    FinishSubmit();
1575  }
1576}
1577
1578Profile* AutofillDialogControllerImpl::profile() {
1579  return profile_;
1580}
1581
1582content::WebContents* AutofillDialogControllerImpl::web_contents() {
1583  return contents_;
1584}
1585
1586////////////////////////////////////////////////////////////////////////////////
1587// AutofillPopupDelegate implementation.
1588
1589void AutofillDialogControllerImpl::OnPopupShown(
1590    content::KeyboardListener* listener) {
1591  GetMetricLogger().LogDialogPopupEvent(
1592      GetDialogType(), AutofillMetrics::DIALOG_POPUP_SHOWN);
1593}
1594
1595void AutofillDialogControllerImpl::OnPopupHidden(
1596    content::KeyboardListener* listener) {}
1597
1598void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier) {
1599  // TODO(estade): implement.
1600}
1601
1602void AutofillDialogControllerImpl::DidAcceptSuggestion(const string16& value,
1603                                                       int identifier) {
1604  const PersonalDataManager::GUIDPair& pair = popup_guids_[identifier];
1605
1606  scoped_ptr<DataModelWrapper> wrapper;
1607  if (IsCreditCardType(input_showing_popup_->type)) {
1608    wrapper.reset(new AutofillCreditCardWrapper(
1609        GetManager()->GetCreditCardByGUID(pair.first)));
1610  } else {
1611    wrapper.reset(new AutofillProfileWrapper(
1612        GetManager()->GetProfileByGUID(pair.first), pair.second));
1613  }
1614
1615  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1616    DialogSection section = static_cast<DialogSection>(i);
1617    wrapper->FillInputs(MutableRequestedFieldsForSection(section));
1618    view_->FillSection(section, *input_showing_popup_);
1619  }
1620
1621  GetMetricLogger().LogDialogPopupEvent(
1622      GetDialogType(), AutofillMetrics::DIALOG_POPUP_FORM_FILLED);
1623
1624  // TODO(estade): not sure why it's necessary to do this explicitly.
1625  HidePopup();
1626}
1627
1628void AutofillDialogControllerImpl::RemoveSuggestion(const string16& value,
1629                                                    int identifier) {
1630  // TODO(estade): implement.
1631}
1632
1633void AutofillDialogControllerImpl::ClearPreviewedForm() {
1634  // TODO(estade): implement.
1635}
1636
1637////////////////////////////////////////////////////////////////////////////////
1638// content::NotificationObserver implementation.
1639
1640void AutofillDialogControllerImpl::Observe(
1641    int type,
1642    const content::NotificationSource& source,
1643    const content::NotificationDetails& details) {
1644  DCHECK_EQ(type, content::NOTIFICATION_NAV_ENTRY_COMMITTED);
1645  content::LoadCommittedDetails* load_details =
1646      content::Details<content::LoadCommittedDetails>(details).ptr();
1647  if (wallet::IsSignInContinueUrl(load_details->entry->GetVirtualURL())) {
1648    should_show_wallet_promo_ = false;
1649    HideSignIn();
1650    account_chooser_model_.SelectActiveWalletAccount();
1651    GetWalletItems();
1652  }
1653}
1654
1655////////////////////////////////////////////////////////////////////////////////
1656// SuggestionsMenuModelDelegate implementation.
1657
1658void AutofillDialogControllerImpl::SuggestionItemSelected(
1659    SuggestionsMenuModel* model,
1660    size_t index) {
1661  if (model->GetItemKeyAt(index) == kManageItemsKey) {
1662    GURL url;
1663    if (!IsPayingWithWallet()) {
1664      GURL settings_url(chrome::kChromeUISettingsURL);
1665      url = settings_url.Resolve(chrome::kAutofillSubPage);
1666    } else {
1667      url = SectionForSuggestionsMenuModel(*model) == SECTION_SHIPPING ?
1668          wallet::GetManageAddressesUrl() : wallet::GetManageInstrumentsUrl();
1669    }
1670
1671    OpenTabWithUrl(url);
1672    return;
1673  }
1674
1675  model->SetCheckedIndex(index);
1676  PrepareDetailInputsForSection(SectionForSuggestionsMenuModel(*model));
1677
1678  LogSuggestionItemSelectedMetric(*model);
1679}
1680
1681////////////////////////////////////////////////////////////////////////////////
1682// wallet::WalletClientDelegate implementation.
1683
1684const AutofillMetrics& AutofillDialogControllerImpl::GetMetricLogger() const {
1685  return metric_logger_;
1686}
1687
1688DialogType AutofillDialogControllerImpl::GetDialogType() const {
1689  return dialog_type_;
1690}
1691
1692std::string AutofillDialogControllerImpl::GetRiskData() const {
1693  return risk_data_.empty() ? "no pagers" : risk_data_;
1694}
1695
1696void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
1697  DCHECK(is_submitting_ && IsPayingWithWallet());
1698
1699  has_accepted_legal_documents_ = true;
1700  LoadRiskFingerprintData();
1701}
1702
1703void AutofillDialogControllerImpl::OnDidAuthenticateInstrument(bool success) {
1704  DCHECK(is_submitting_ && IsPayingWithWallet());
1705
1706  // TODO(dbeam): use the returned full wallet. b/8332329
1707  if (success)
1708    GetFullWallet();
1709  else
1710    DisableWallet();
1711}
1712
1713void AutofillDialogControllerImpl::OnDidGetFullWallet(
1714    scoped_ptr<wallet::FullWallet> full_wallet) {
1715  DCHECK(is_submitting_ && IsPayingWithWallet());
1716
1717  full_wallet_ = full_wallet.Pass();
1718
1719  if (full_wallet_->required_actions().empty()) {
1720    FinishSubmit();
1721    return;
1722  }
1723
1724  switch (full_wallet_->required_actions()[0]) {
1725    case wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS:
1726      choose_another_instrument_or_address_ = true;
1727      SetIsSubmitting(false);
1728      view_->UpdateNotificationArea();
1729      view_->UpdateButtonStrip();
1730      GetWalletItems();
1731      break;
1732
1733    case wallet::VERIFY_CVV:
1734      SuggestionsUpdated();
1735      view_->UpdateNotificationArea();
1736      view_->UpdateButtonStrip();
1737      break;
1738
1739    default:
1740      DisableWallet();
1741      break;
1742  }
1743}
1744
1745void AutofillDialogControllerImpl::OnPassiveSigninSuccess(
1746    const std::string& username) {
1747  const string16 username16 = UTF8ToUTF16(username);
1748  signin_helper_.reset();
1749  account_chooser_model_.SetActiveWalletAccountName(username16);
1750  GetWalletItems();
1751}
1752
1753void AutofillDialogControllerImpl::OnUserNameFetchSuccess(
1754    const std::string& username) {
1755  const string16 username16 = UTF8ToUTF16(username);
1756  signin_helper_.reset();
1757  account_chooser_model_.SetActiveWalletAccountName(username16);
1758  OnWalletOrSigninUpdate();
1759}
1760
1761void AutofillDialogControllerImpl::OnAutomaticSigninSuccess(
1762    const std::string& username) {
1763  NOTIMPLEMENTED();
1764}
1765
1766void AutofillDialogControllerImpl::OnPassiveSigninFailure(
1767    const GoogleServiceAuthError& error) {
1768  // TODO(aruslan): report an error.
1769  LOG(ERROR) << "failed to passively sign in: " << error.ToString();
1770  OnWalletSigninError();
1771}
1772
1773void AutofillDialogControllerImpl::OnUserNameFetchFailure(
1774    const GoogleServiceAuthError& error) {
1775  // TODO(aruslan): report an error.
1776  LOG(ERROR) << "failed to fetch the user account name: " << error.ToString();
1777  OnWalletSigninError();
1778}
1779
1780void AutofillDialogControllerImpl::OnAutomaticSigninFailure(
1781    const GoogleServiceAuthError& error) {
1782  // TODO(aruslan): report an error.
1783  LOG(ERROR) << "failed to automatically sign in: " << error.ToString();
1784  OnWalletSigninError();
1785}
1786
1787void AutofillDialogControllerImpl::OnDidGetWalletItems(
1788    scoped_ptr<wallet::WalletItems> wallet_items) {
1789  legal_documents_text_.clear();
1790  legal_document_link_ranges_.clear();
1791  has_accepted_legal_documents_ = false;
1792
1793  wallet_items_ = wallet_items.Pass();
1794  OnWalletOrSigninUpdate();
1795}
1796
1797void AutofillDialogControllerImpl::OnDidSaveAddress(
1798    const std::string& address_id,
1799    const std::vector<wallet::RequiredAction>& required_actions) {
1800  DCHECK(is_submitting_ && IsPayingWithWallet());
1801
1802  if (required_actions.empty()) {
1803    active_address_id_ = address_id;
1804    GetFullWalletIfReady();
1805  } else {
1806    HandleSaveOrUpdateRequiredActions(required_actions);
1807  }
1808}
1809
1810void AutofillDialogControllerImpl::OnDidSaveInstrument(
1811    const std::string& instrument_id,
1812    const std::vector<wallet::RequiredAction>& required_actions) {
1813  DCHECK(is_submitting_ && IsPayingWithWallet());
1814
1815  if (required_actions.empty()) {
1816    active_instrument_id_ = instrument_id;
1817    GetFullWalletIfReady();
1818  } else {
1819    HandleSaveOrUpdateRequiredActions(required_actions);
1820  }
1821}
1822
1823void AutofillDialogControllerImpl::OnDidSaveInstrumentAndAddress(
1824    const std::string& instrument_id,
1825    const std::string& address_id,
1826    const std::vector<wallet::RequiredAction>& required_actions) {
1827  OnDidSaveInstrument(instrument_id, required_actions);
1828  // |is_submitting_| can change while in |OnDidSaveInstrument()|.
1829  if (is_submitting_)
1830    OnDidSaveAddress(address_id, required_actions);
1831}
1832
1833void AutofillDialogControllerImpl::OnDidUpdateAddress(
1834    const std::string& address_id,
1835    const std::vector<wallet::RequiredAction>& required_actions) {
1836  OnDidSaveAddress(address_id, required_actions);
1837}
1838
1839void AutofillDialogControllerImpl::OnDidUpdateInstrument(
1840    const std::string& instrument_id,
1841    const std::vector<wallet::RequiredAction>& required_actions) {
1842  OnDidSaveInstrument(instrument_id, required_actions);
1843}
1844
1845void AutofillDialogControllerImpl::OnWalletError(
1846    wallet::WalletClient::ErrorType error_type) {
1847  // TODO(dbeam): Do something with |error_type|. http://crbug.com/164410
1848  DisableWallet();
1849}
1850
1851void AutofillDialogControllerImpl::OnMalformedResponse() {
1852  DisableWallet();
1853}
1854
1855void AutofillDialogControllerImpl::OnNetworkError(int response_code) {
1856  DisableWallet();
1857}
1858
1859////////////////////////////////////////////////////////////////////////////////
1860// PersonalDataManagerObserver implementation.
1861
1862void AutofillDialogControllerImpl::OnPersonalDataChanged() {
1863  if (is_submitting_)
1864    return;
1865
1866  SuggestionsUpdated();
1867}
1868
1869////////////////////////////////////////////////////////////////////////////////
1870// AccountChooserModelDelegate implementation.
1871
1872void AutofillDialogControllerImpl::AccountChoiceChanged() {
1873  if (is_submitting_)
1874    GetWalletClient()->CancelRequests();
1875
1876  SetIsSubmitting(false);
1877
1878  SuggestionsUpdated();
1879  UpdateAccountChooserView();
1880}
1881
1882void AutofillDialogControllerImpl::UpdateAccountChooserView() {
1883  if (view_) {
1884    view_->UpdateAccountChooser();
1885    view_->UpdateNotificationArea();
1886  }
1887}
1888
1889////////////////////////////////////////////////////////////////////////////////
1890
1891bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
1892    const content::NativeWebKeyboardEvent& event) {
1893  if (popup_controller_.get())
1894    return popup_controller_->HandleKeyPressEvent(event);
1895
1896  return false;
1897}
1898
1899bool AutofillDialogControllerImpl::RequestingCreditCardInfo() const {
1900  DCHECK_GT(form_structure_.field_count(), 0U);
1901
1902  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1903    if (IsCreditCardType(form_structure_.field(i)->type()))
1904      return true;
1905  }
1906
1907  return false;
1908}
1909
1910bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
1911  return source_url_.SchemeIs(chrome::kHttpsScheme) &&
1912         !net::IsCertStatusError(ssl_status_.cert_status) &&
1913         !net::IsCertStatusMinorError(ssl_status_.cert_status);
1914}
1915
1916AutofillDialogControllerImpl::AutofillDialogControllerImpl(
1917    content::WebContents* contents,
1918    const FormData& form_structure,
1919    const GURL& source_url,
1920    const DialogType dialog_type,
1921    const base::Callback<void(const FormStructure*,
1922                              const std::string&)>& callback)
1923    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
1924      contents_(contents),
1925      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
1926      dialog_type_(dialog_type),
1927      form_structure_(form_structure, std::string()),
1928      invoked_from_same_origin_(true),
1929      source_url_(source_url),
1930      ssl_status_(form_structure.ssl_status),
1931      callback_(callback),
1932      account_chooser_model_(this, profile_->GetPrefs(), metric_logger_,
1933                             dialog_type),
1934      wallet_client_(profile_->GetRequestContext(), this),
1935      suggested_email_(this),
1936      suggested_cc_(this),
1937      suggested_billing_(this),
1938      suggested_cc_billing_(this),
1939      suggested_shipping_(this),
1940      cares_about_shipping_(true),
1941      input_showing_popup_(NULL),
1942      weak_ptr_factory_(this),
1943      should_show_wallet_promo_(!profile_->GetPrefs()->GetBoolean(
1944          ::prefs::kAutofillDialogHasPaidWithWallet)),
1945      has_shown_wallet_usage_confirmation_(false),
1946      has_accepted_legal_documents_(false),
1947      is_submitting_(false),
1948      wallet_server_validation_error_(false),
1949      choose_another_instrument_or_address_(false),
1950      autocheckout_state_(AUTOCHECKOUT_NOT_STARTED),
1951      was_ui_latency_logged_(false) {
1952  // TODO(estade): remove duplicates from |form_structure|?
1953  DCHECK(!callback_.is_null());
1954}
1955
1956AutofillDialogView* AutofillDialogControllerImpl::CreateView() {
1957  return AutofillDialogView::Create(this);
1958}
1959
1960PersonalDataManager* AutofillDialogControllerImpl::GetManager() {
1961  return PersonalDataManagerFactory::GetForProfile(profile_);
1962}
1963
1964wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient() {
1965  return &wallet_client_;
1966}
1967
1968bool AutofillDialogControllerImpl::IsPayingWithWallet() const {
1969  return account_chooser_model_.WalletIsSelected() &&
1970         SignedInState() == SIGNED_IN;
1971}
1972
1973void AutofillDialogControllerImpl::LoadRiskFingerprintData() {
1974  DCHECK(AreLegalDocumentsCurrent());
1975
1976  // Clear potential stale data to ensure |GetFullWalletIfReady()| triggers only
1977  // when a new fingerprint is loaded.
1978  risk_data_.clear();
1979
1980  uint64 obfuscated_gaia_id = 0;
1981  bool success = base::StringToUint64(wallet_items_->obfuscated_gaia_id(),
1982                                      &obfuscated_gaia_id);
1983  DCHECK(success);
1984
1985  gfx::Rect window_bounds;
1986#if !defined(OS_ANDROID)
1987  window_bounds = GetBaseWindowForWebContents(web_contents())->GetBounds();
1988#else
1989  // TODO(dbeam): figure out the correct browser window size to pass along for
1990  // android.
1991#endif
1992
1993  PrefService* user_prefs = profile_->GetPrefs();
1994  std::string charset = user_prefs->GetString(::prefs::kDefaultCharset);
1995  std::string accept_languages =
1996      user_prefs->GetString(::prefs::kAcceptLanguages);
1997  base::Time install_time = base::Time::FromTimeT(
1998      g_browser_process->local_state()->GetInt64(::prefs::kInstallDate));
1999
2000  risk::GetFingerprint(
2001      obfuscated_gaia_id, window_bounds, *web_contents(),
2002      chrome::VersionInfo().Version(), charset, accept_languages, install_time,
2003      dialog_type_, g_browser_process->GetApplicationLocale(),
2004      base::Bind(&AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData,
2005                 weak_ptr_factory_.GetWeakPtr()));
2006}
2007
2008void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
2009    scoped_ptr<risk::Fingerprint> fingerprint) {
2010  DCHECK(AreLegalDocumentsCurrent());
2011
2012  std::string proto_data;
2013  fingerprint->SerializeToString(&proto_data);
2014  bool success = base::Base64Encode(proto_data, &risk_data_);
2015  DCHECK(success);
2016
2017  GetFullWalletIfReady();
2018}
2019
2020void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL& url) {
2021#if !defined(OS_ANDROID)
2022  chrome::NavigateParams params(
2023      chrome::FindBrowserWithWebContents(web_contents()),
2024      url,
2025      content::PAGE_TRANSITION_AUTO_BOOKMARK);
2026  params.disposition = NEW_FOREGROUND_TAB;
2027  chrome::Navigate(&params);
2028#else
2029  // TODO(estade): use TabModelList?
2030#endif
2031}
2032
2033void AutofillDialogControllerImpl::OnWalletSigninError() {
2034  signin_helper_.reset();
2035  account_chooser_model_.SetHadWalletSigninError();
2036  GetWalletClient()->CancelRequests();
2037  LogDialogLatencyToShow();
2038}
2039
2040void AutofillDialogControllerImpl::DisableWallet() {
2041  signin_helper_.reset();
2042  wallet_items_.reset();
2043  GetWalletClient()->CancelRequests();
2044  SetIsSubmitting(false);
2045  account_chooser_model_.SetHadWalletError();
2046}
2047
2048void AutofillDialogControllerImpl::SuggestionsUpdated() {
2049  suggested_email_.Reset();
2050  suggested_cc_.Reset();
2051  suggested_billing_.Reset();
2052  suggested_cc_billing_.Reset();
2053  suggested_shipping_.Reset();
2054  HidePopup();
2055
2056  suggested_shipping_.AddKeyedItem(
2057      kSameAsBillingKey,
2058      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING));
2059
2060  if (IsPayingWithWallet()) {
2061    if (!account_chooser_model_.active_wallet_account_name().empty()) {
2062      suggested_email_.AddKeyedItem(
2063          base::IntToString(0),
2064          account_chooser_model_.active_wallet_account_name());
2065    }
2066
2067    const std::vector<wallet::Address*>& addresses =
2068        wallet_items_->addresses();
2069    for (size_t i = 0; i < addresses.size(); ++i) {
2070      std::string key = base::IntToString(i);
2071      suggested_shipping_.AddKeyedItemWithSublabel(
2072          key,
2073          addresses[i]->DisplayName(),
2074          addresses[i]->DisplayNameDetail());
2075
2076      if (addresses[i]->object_id() == wallet_items_->default_address_id())
2077        suggested_shipping_.SetCheckedItem(key);
2078    }
2079
2080    if (!IsSubmitPausedOn(wallet::VERIFY_CVV)) {
2081      const std::vector<wallet::WalletItems::MaskedInstrument*>& instruments =
2082          wallet_items_->instruments();
2083      std::string first_active_instrument_key;
2084      std::string default_instrument_key;
2085      for (size_t i = 0; i < instruments.size(); ++i) {
2086        bool allowed = IsInstrumentAllowed(*instruments[i]);
2087        gfx::Image icon = instruments[i]->CardIcon();
2088        if (!allowed && !icon.IsEmpty()) {
2089          // Create a grayed disabled icon.
2090          SkBitmap disabled_bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
2091              *icon.ToSkBitmap(), kGrayImageShift);
2092          icon = gfx::Image(
2093              gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap));
2094        }
2095        std::string key = base::IntToString(i);
2096        suggested_cc_billing_.AddKeyedItemWithSublabelAndIcon(
2097            key,
2098            instruments[i]->DisplayName(),
2099            instruments[i]->DisplayNameDetail(),
2100            icon);
2101        suggested_cc_billing_.SetEnabled(key, allowed);
2102
2103        if (allowed) {
2104          if (first_active_instrument_key.empty())
2105            first_active_instrument_key = key;
2106          if (instruments[i]->object_id() ==
2107              wallet_items_->default_instrument_id()) {
2108            default_instrument_key = key;
2109          }
2110        }
2111      }
2112
2113      // TODO(estade): this should have a URL sublabel.
2114      suggested_cc_billing_.AddKeyedItem(
2115          kAddNewItemKey,
2116          l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_DETAILS));
2117      if (!wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2118        suggested_cc_billing_.AddKeyedItem(
2119            kManageItemsKey,
2120            l10n_util::GetStringUTF16(
2121                IDS_AUTOFILL_DIALOG_MANAGE_BILLING_DETAILS));
2122      }
2123
2124      // Determine which instrument item should be selected.
2125      if (!default_instrument_key.empty())
2126        suggested_cc_billing_.SetCheckedItem(default_instrument_key);
2127      else if (!first_active_instrument_key.empty())
2128        suggested_cc_billing_.SetCheckedItem(first_active_instrument_key);
2129      else
2130        suggested_cc_billing_.SetCheckedItem(kAddNewItemKey);
2131    }
2132  } else {
2133    PersonalDataManager* manager = GetManager();
2134    const std::vector<CreditCard*>& cards = manager->GetCreditCards();
2135    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
2136    for (size_t i = 0; i < cards.size(); ++i) {
2137      if (!HasCompleteAndVerifiedData(*cards[i], requested_cc_fields_))
2138        continue;
2139
2140      suggested_cc_.AddKeyedItemWithIcon(
2141          cards[i]->guid(),
2142          cards[i]->Label(),
2143          rb.GetImageNamed(cards[i]->IconResourceId()));
2144    }
2145
2146    const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
2147    const std::string app_locale = g_browser_process->GetApplicationLocale();
2148    for (size_t i = 0; i < profiles.size(); ++i) {
2149      if (!HasCompleteAndVerifiedData(*profiles[i], requested_shipping_fields_))
2150        continue;
2151
2152      // Add all email addresses.
2153      std::vector<string16> values;
2154      profiles[i]->GetMultiInfo(EMAIL_ADDRESS, app_locale, &values);
2155      for (size_t j = 0; j < values.size(); ++j) {
2156        if (!values[j].empty())
2157          suggested_email_.AddKeyedItem(profiles[i]->guid(), values[j]);
2158      }
2159
2160      // Don't add variants for addresses: the email variants are handled above,
2161      // name is part of credit card and we'll just ignore phone number
2162      // variants.
2163      suggested_billing_.AddKeyedItem(profiles[i]->guid(),
2164                                      profiles[i]->Label());
2165      suggested_shipping_.AddKeyedItem(profiles[i]->guid(),
2166                                       profiles[i]->Label());
2167    }
2168
2169    suggested_cc_.AddKeyedItem(
2170        kAddNewItemKey,
2171        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD));
2172    suggested_cc_.AddKeyedItem(
2173        kManageItemsKey,
2174        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD));
2175    suggested_billing_.AddKeyedItem(
2176        kAddNewItemKey,
2177        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS));
2178    suggested_billing_.AddKeyedItem(
2179        kManageItemsKey,
2180        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS));
2181  }
2182
2183  suggested_email_.AddKeyedItem(
2184      kAddNewItemKey,
2185      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_EMAIL_ADDRESS));
2186  if (!IsPayingWithWallet()) {
2187    suggested_email_.AddKeyedItem(
2188        kManageItemsKey,
2189        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_EMAIL_ADDRESS));
2190  }
2191
2192  suggested_shipping_.AddKeyedItem(
2193      kAddNewItemKey,
2194      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS));
2195  if (!IsPayingWithWallet() ||
2196      !wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2197    suggested_shipping_.AddKeyedItem(
2198        kManageItemsKey,
2199        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS));
2200  }
2201
2202  if (!IsPayingWithWallet()) {
2203    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2204      DialogSection section = static_cast<DialogSection>(i);
2205      if (!SectionIsActive(section))
2206        continue;
2207
2208      // Set the starting choice for the menu. First set to the default in case
2209      // the GUID saved in prefs refers to a profile that no longer exists.
2210      std::string guid;
2211      int variant;
2212      GetDefaultAutofillChoice(section, &guid, &variant);
2213      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2214      model->SetCheckedItemNthWithKey(guid, variant + 1);
2215      if (GetAutofillChoice(section, &guid, &variant))
2216        model->SetCheckedItemNthWithKey(guid, variant + 1);
2217    }
2218  }
2219
2220  if (view_)
2221    view_->ModelChanged();
2222
2223  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2224    PrepareDetailInputsForSection(static_cast<DialogSection>(section));
2225  }
2226}
2227
2228void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
2229    DialogSection section,
2230    const InputFieldComparator& compare) {
2231  const DetailInputs& inputs = RequestedFieldsForSection(section);
2232
2233  // Email is hidden while using Wallet, special case it.
2234  if (section == SECTION_EMAIL && IsPayingWithWallet()) {
2235    AutofillProfile profile;
2236    profile.SetRawInfo(EMAIL_ADDRESS,
2237                       account_chooser_model_.active_wallet_account_name());
2238    AutofillProfileWrapper profile_wrapper(&profile, 0);
2239    profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2240    return;
2241  }
2242
2243  if (!SectionIsActive(section))
2244    return;
2245
2246  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2247  if (wrapper) {
2248    // Only fill in data that is associated with this section.
2249    const DetailInputs& inputs = RequestedFieldsForSection(section);
2250    wrapper->FillFormStructure(inputs, compare, &form_structure_);
2251
2252    // CVC needs special-casing because the CreditCard class doesn't store or
2253    // handle them. This isn't necessary when filling the combined CC and
2254    // billing section as CVC comes from |full_wallet_| in this case.
2255    if (section == SECTION_CC)
2256      SetCvcResult(view_->GetCvc());
2257  } else {
2258    // The user manually input data. If using Autofill, save the info as new or
2259    // edited data. Always fill local data into |form_structure_|.
2260    DetailOutputMap output;
2261    view_->GetUserInput(section, &output);
2262
2263    if (section == SECTION_CC) {
2264      CreditCard card;
2265      card.set_origin(kAutofillDialogOrigin);
2266      FillFormGroupFromOutputs(output, &card);
2267
2268      if (ShouldSaveDetailsLocally())
2269        GetManager()->SaveImportedCreditCard(card);
2270
2271      AutofillCreditCardWrapper card_wrapper(&card);
2272      card_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2273
2274      // Again, CVC needs special-casing. Fill it in directly from |output|.
2275      SetCvcResult(GetValueForType(output, CREDIT_CARD_VERIFICATION_CODE));
2276    } else {
2277      AutofillProfile profile;
2278      profile.set_origin(kAutofillDialogOrigin);
2279      FillFormGroupFromOutputs(output, &profile);
2280
2281      // For billing, the profile name has to come from the CC section.
2282      if (section == SECTION_BILLING) {
2283        profile.SetRawInfo(NAME_FULL,
2284                           GetValueFromSection(SECTION_CC, CREDIT_CARD_NAME));
2285        profile.SetRawInfo(EMAIL_ADDRESS,
2286                           GetValueFromSection(SECTION_EMAIL, EMAIL_ADDRESS));
2287      }
2288
2289      if (ShouldSaveDetailsLocally())
2290        SaveProfileGleanedFromSection(profile, section);
2291
2292      AutofillProfileWrapper profile_wrapper(&profile, 0);
2293      profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2294    }
2295  }
2296}
2297
2298void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) {
2299  FillOutputForSectionWithComparator(section,
2300                                     base::Bind(DetailInputMatchesField));
2301}
2302
2303bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
2304    DialogSection section) const {
2305  // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
2306  // any of the fields.
2307  // TODO(estade): remove !IsPayingWithWallet() check once WalletClient support
2308  // is added. http://crbug.com/243514
2309  if (section == SECTION_SHIPPING && !IsPayingWithWallet())
2310    return cares_about_shipping_;
2311
2312  return true;
2313}
2314
2315void AutofillDialogControllerImpl::SetCvcResult(const string16& cvc) {
2316  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
2317    AutofillField* field = form_structure_.field(i);
2318    if (field->type() == CREDIT_CARD_VERIFICATION_CODE) {
2319      field->value = cvc;
2320      break;
2321    }
2322  }
2323}
2324
2325string16 AutofillDialogControllerImpl::GetValueFromSection(
2326    DialogSection section,
2327    AutofillFieldType type) {
2328  DCHECK(SectionIsActive(section));
2329
2330  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2331  if (wrapper)
2332    return wrapper->GetInfo(type);
2333
2334  DetailOutputMap output;
2335  view_->GetUserInput(section, &output);
2336  for (DetailOutputMap::iterator iter = output.begin(); iter != output.end();
2337       ++iter) {
2338    if (iter->first->type == type)
2339      return iter->second;
2340  }
2341
2342  return string16();
2343}
2344
2345void AutofillDialogControllerImpl::SaveProfileGleanedFromSection(
2346    const AutofillProfile& profile,
2347    DialogSection section) {
2348  if (section == SECTION_EMAIL) {
2349    // Save the email address to the existing (suggested) billing profile. If
2350    // there is no existing profile, the newly created one will pick up this
2351    // email, so in that case do nothing.
2352    scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(SECTION_BILLING);
2353    if (wrapper) {
2354      std::string item_key = SuggestionsMenuModelForSection(SECTION_BILLING)->
2355          GetItemKeyForCheckedItem();
2356      AutofillProfile* billing_profile =
2357          GetManager()->GetProfileByGUID(item_key);
2358      billing_profile->OverwriteWithOrAddTo(
2359          profile,
2360          g_browser_process->GetApplicationLocale());
2361    }
2362  } else {
2363    GetManager()->SaveImportedProfile(profile);
2364  }
2365}
2366
2367SuggestionsMenuModel* AutofillDialogControllerImpl::
2368    SuggestionsMenuModelForSection(DialogSection section) {
2369  switch (section) {
2370    case SECTION_EMAIL:
2371      return &suggested_email_;
2372    case SECTION_CC:
2373      return &suggested_cc_;
2374    case SECTION_BILLING:
2375      return &suggested_billing_;
2376    case SECTION_SHIPPING:
2377      return &suggested_shipping_;
2378    case SECTION_CC_BILLING:
2379      return &suggested_cc_billing_;
2380  }
2381
2382  NOTREACHED();
2383  return NULL;
2384}
2385
2386const SuggestionsMenuModel* AutofillDialogControllerImpl::
2387    SuggestionsMenuModelForSection(DialogSection section) const {
2388  return const_cast<AutofillDialogControllerImpl*>(this)->
2389      SuggestionsMenuModelForSection(section);
2390}
2391
2392DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
2393    const SuggestionsMenuModel& model) {
2394  if (&model == &suggested_email_)
2395    return SECTION_EMAIL;
2396
2397  if (&model == &suggested_cc_)
2398    return SECTION_CC;
2399
2400  if (&model == &suggested_billing_)
2401    return SECTION_BILLING;
2402
2403  if (&model == &suggested_cc_billing_)
2404    return SECTION_CC_BILLING;
2405
2406  DCHECK_EQ(&model, &suggested_shipping_);
2407  return SECTION_SHIPPING;
2408}
2409
2410DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
2411    DialogSection section) {
2412  return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
2413}
2414
2415void AutofillDialogControllerImpl::HidePopup() {
2416  if (popup_controller_.get())
2417    popup_controller_->Hide();
2418  input_showing_popup_ = NULL;
2419}
2420
2421bool AutofillDialogControllerImpl::IsEditingExistingData(
2422    DialogSection section) const {
2423  return section_editing_state_.count(section) > 0;
2424}
2425
2426void AutofillDialogControllerImpl::SetEditingExistingData(
2427    DialogSection section, bool editing) {
2428  if (editing)
2429    section_editing_state_.insert(section);
2430  else
2431    section_editing_state_.erase(section);
2432}
2433
2434bool AutofillDialogControllerImpl::IsManuallyEditingSection(
2435    DialogSection section) const {
2436  return IsEditingExistingData(section) ||
2437         SuggestionsMenuModelForSection(section)->
2438             GetItemKeyForCheckedItem() == kAddNewItemKey;
2439}
2440
2441bool AutofillDialogControllerImpl::IsASuggestionItemKey(
2442    const std::string& key) const {
2443  return !key.empty() &&
2444      key != kAddNewItemKey &&
2445      key != kManageItemsKey &&
2446      key != kSameAsBillingKey;
2447}
2448
2449bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
2450  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2451    if (IsManuallyEditingSection(static_cast<DialogSection>(section)))
2452      return true;
2453  }
2454  return false;
2455}
2456
2457bool AutofillDialogControllerImpl::InputIsEditable(
2458    const DetailInput& input,
2459    DialogSection section) const {
2460  if (input.type != CREDIT_CARD_NUMBER || !IsPayingWithWallet())
2461    return true;
2462
2463  if (IsEditingExistingData(section))
2464    return false;
2465
2466  return true;
2467}
2468
2469bool AutofillDialogControllerImpl::AllSectionsAreValid() const {
2470  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2471    if (!SectionIsValid(static_cast<DialogSection>(section)))
2472      return false;
2473  }
2474  return true;
2475}
2476
2477bool AutofillDialogControllerImpl::SectionIsValid(
2478    DialogSection section) const {
2479  if (!IsManuallyEditingSection(section))
2480    return true;
2481
2482  DetailOutputMap detail_outputs;
2483  view_->GetUserInput(section, &detail_outputs);
2484  return InputsAreValid(detail_outputs, VALIDATE_EDIT).empty();
2485}
2486
2487bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
2488    const base::string16& year,
2489    const base::string16& month) const {
2490  // If the expiration is in the past as per the local clock, it's invalid.
2491  base::Time now = base::Time::Now();
2492  if (!autofill::IsValidCreditCardExpirationDate(year, month, now))
2493    return false;
2494
2495  if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING)) {
2496    const wallet::WalletItems::MaskedInstrument* instrument =
2497        ActiveInstrument();
2498    const std::string& locale = g_browser_process->GetApplicationLocale();
2499    int month_int;
2500    if (base::StringToInt(month, &month_int) &&
2501        instrument->status() ==
2502            wallet::WalletItems::MaskedInstrument::EXPIRED &&
2503        year == instrument->GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, locale) &&
2504        month_int == instrument->expiration_month()) {
2505      // Otherwise, if the user is editing an instrument that's deemed expired
2506      // by the Online Wallet server, mark it invalid on selection.
2507      return false;
2508    }
2509  }
2510
2511  return true;
2512}
2513
2514bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
2515  return SectionIsActive(SECTION_SHIPPING) &&
2516      suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey;
2517}
2518
2519bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
2520  // It's possible that the user checked [X] Save details locally before
2521  // switching payment methods, so only ask the view whether to save details
2522  // locally if that checkbox is showing (currently if not paying with wallet).
2523  // Also, if the user isn't editing any sections, there's no data to save
2524  // locally.
2525  return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
2526}
2527
2528void AutofillDialogControllerImpl::SetIsSubmitting(bool submitting) {
2529  is_submitting_ = submitting;
2530
2531  if (!submitting)
2532    full_wallet_.reset();
2533
2534  if (view_) {
2535    view_->UpdateButtonStrip();
2536    view_->UpdateNotificationArea();
2537  }
2538}
2539
2540bool AutofillDialogControllerImpl::AreLegalDocumentsCurrent() const {
2541  return has_accepted_legal_documents_ ||
2542      (wallet_items_ && wallet_items_->legal_documents().empty());
2543}
2544
2545void AutofillDialogControllerImpl::SubmitWithWallet() {
2546  // TODO(dbeam): disallow interacting with the dialog while submitting.
2547  // http://crbug.com/230932
2548
2549  active_instrument_id_.clear();
2550  active_address_id_.clear();
2551  full_wallet_.reset();
2552
2553  content::BrowserThread::PostTask(
2554     content::BrowserThread::IO, FROM_HERE,
2555     base::Bind(&UserDidOptIntoLocationServices));
2556
2557  GetWalletClient()->AcceptLegalDocuments(
2558      wallet_items_->legal_documents(),
2559      wallet_items_->google_transaction_id(),
2560      source_url_);
2561
2562  if (AreLegalDocumentsCurrent())
2563    LoadRiskFingerprintData();
2564
2565  const wallet::WalletItems::MaskedInstrument* active_instrument =
2566      ActiveInstrument();
2567  if (!IsManuallyEditingSection(SECTION_CC_BILLING)) {
2568    active_instrument_id_ = active_instrument->object_id();
2569    DCHECK(!active_instrument_id_.empty());
2570  }
2571
2572  const wallet::Address* active_address = ActiveShippingAddress();
2573  if (!IsManuallyEditingSection(SECTION_SHIPPING) &&
2574      !ShouldUseBillingForShipping()) {
2575    active_address_id_ = active_address->object_id();
2576    DCHECK(!active_address_id_.empty());
2577  }
2578
2579  scoped_ptr<wallet::Instrument> inputted_instrument =
2580      CreateTransientInstrument();
2581  scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest> update_request =
2582      CreateUpdateInstrumentRequest(
2583          inputted_instrument.get(),
2584          !IsEditingExistingData(SECTION_CC_BILLING) ? std::string() :
2585              active_instrument->object_id());
2586
2587  scoped_ptr<wallet::Address> inputted_address;
2588  if (active_address_id_.empty()) {
2589    if (ShouldUseBillingForShipping()) {
2590      const wallet::Address& address = inputted_instrument ?
2591          inputted_instrument->address() : active_instrument->address();
2592      // Try to find an exact matched shipping address and use it for shipping,
2593      // otherwise save it as a new shipping address. http://crbug.com/225442
2594      const wallet::Address* duplicated_address =
2595          FindDuplicateAddress(wallet_items_->addresses(), address);
2596      if (duplicated_address) {
2597        active_address_id_ = duplicated_address->object_id();
2598        DCHECK(!active_address_id_.empty());
2599      } else {
2600        inputted_address.reset(new wallet::Address(address));
2601        DCHECK(inputted_address->object_id().empty());
2602      }
2603    } else {
2604      inputted_address = CreateTransientAddress();
2605      if (IsEditingExistingData(SECTION_SHIPPING)) {
2606        inputted_address->set_object_id(active_address->object_id());
2607        DCHECK(!inputted_address->object_id().empty());
2608      }
2609    }
2610  }
2611
2612  // If there's neither an address nor instrument to save, |GetFullWallet()|
2613  // is called when the risk fingerprint is loaded.
2614  if (!active_instrument_id_.empty() && !active_address_id_.empty())
2615    return;
2616
2617  // If instrument and address aren't based off of any existing data, save both.
2618  if (inputted_instrument && inputted_address && !update_request &&
2619      inputted_address->object_id().empty()) {
2620    GetWalletClient()->SaveInstrumentAndAddress(
2621        *inputted_instrument,
2622        *inputted_address,
2623        wallet_items_->obfuscated_gaia_id(),
2624        source_url_);
2625    return;
2626  }
2627
2628  if (inputted_instrument) {
2629    if (update_request) {
2630      scoped_ptr<wallet::Address> billing_address(
2631          new wallet::Address(inputted_instrument->address()));
2632      GetWalletClient()->UpdateInstrument(*update_request,
2633                                          billing_address.Pass());
2634    } else {
2635      GetWalletClient()->SaveInstrument(*inputted_instrument,
2636                                        wallet_items_->obfuscated_gaia_id(),
2637                                        source_url_);
2638    }
2639  }
2640
2641  if (inputted_address) {
2642    if (!inputted_address->object_id().empty())
2643      GetWalletClient()->UpdateAddress(*inputted_address, source_url_);
2644    else
2645      GetWalletClient()->SaveAddress(*inputted_address, source_url_);
2646  }
2647}
2648
2649scoped_ptr<wallet::Instrument> AutofillDialogControllerImpl::
2650    CreateTransientInstrument() {
2651  if (!active_instrument_id_.empty())
2652    return scoped_ptr<wallet::Instrument>();
2653
2654  DetailOutputMap output;
2655  view_->GetUserInput(SECTION_CC_BILLING, &output);
2656
2657  CreditCard card;
2658  AutofillProfile profile;
2659  string16 cvc;
2660  GetBillingInfoFromOutputs(output, &card, &cvc, &profile);
2661
2662  return scoped_ptr<wallet::Instrument>(
2663      new wallet::Instrument(card, cvc, profile));
2664}
2665
2666scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest>
2667    AutofillDialogControllerImpl::CreateUpdateInstrumentRequest(
2668        const wallet::Instrument* instrument,
2669        const std::string& instrument_id) {
2670  if (!instrument || instrument_id.empty())
2671    return scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest>();
2672
2673  scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest> update_request(
2674      new wallet::WalletClient::UpdateInstrumentRequest(
2675          instrument_id, source_url_));
2676  update_request->expiration_month = instrument->expiration_month();
2677  update_request->expiration_year = instrument->expiration_year();
2678  update_request->card_verification_number =
2679      UTF16ToUTF8(instrument->card_verification_number());
2680  update_request->obfuscated_gaia_id = wallet_items_->obfuscated_gaia_id();
2681  return update_request.Pass();
2682}
2683
2684scoped_ptr<wallet::Address>AutofillDialogControllerImpl::
2685    CreateTransientAddress() {
2686  // If not using billing for shipping, just scrape the view.
2687  DetailOutputMap output;
2688  view_->GetUserInput(SECTION_SHIPPING, &output);
2689
2690  AutofillProfile profile;
2691  FillFormGroupFromOutputs(output, &profile);
2692
2693  return scoped_ptr<wallet::Address>(new wallet::Address(profile));
2694}
2695
2696void AutofillDialogControllerImpl::GetFullWallet() {
2697  DCHECK(is_submitting_);
2698  DCHECK(IsPayingWithWallet());
2699  DCHECK(wallet_items_);
2700  DCHECK(!active_instrument_id_.empty());
2701  DCHECK(!active_address_id_.empty());
2702
2703  std::vector<wallet::WalletClient::RiskCapability> capabilities;
2704  capabilities.push_back(wallet::WalletClient::VERIFY_CVC);
2705
2706  GetWalletClient()->GetFullWallet(wallet::WalletClient::FullWalletRequest(
2707      active_instrument_id_,
2708      active_address_id_,
2709      source_url_,
2710      wallet_items_->google_transaction_id(),
2711      capabilities));
2712}
2713
2714void AutofillDialogControllerImpl::GetFullWalletIfReady() {
2715  DCHECK(is_submitting_);
2716  DCHECK(IsPayingWithWallet());
2717
2718  if (!active_instrument_id_.empty() && !active_address_id_.empty() &&
2719      !risk_data_.empty()) {
2720    GetFullWallet();
2721  }
2722}
2723
2724void AutofillDialogControllerImpl::HandleSaveOrUpdateRequiredActions(
2725    const std::vector<wallet::RequiredAction>& required_actions) {
2726  DCHECK(!required_actions.empty());
2727
2728  for (std::vector<wallet::RequiredAction>::const_iterator iter =
2729           required_actions.begin();
2730       iter != required_actions.end(); ++iter) {
2731    if (*iter == wallet::INVALID_FORM_FIELD) {
2732      wallet_server_validation_error_ = true;
2733    } else {
2734      // TODO(dbeam): handle this more gracefully.
2735      DisableWallet();
2736    }
2737  }
2738
2739  SetIsSubmitting(false);
2740}
2741
2742void AutofillDialogControllerImpl::FinishSubmit() {
2743  FillOutputForSection(SECTION_EMAIL);
2744  FillOutputForSection(SECTION_CC);
2745  FillOutputForSection(SECTION_BILLING);
2746  FillOutputForSection(SECTION_CC_BILLING);
2747
2748  if (ShouldUseBillingForShipping()) {
2749    FillOutputForSectionWithComparator(
2750        SECTION_BILLING,
2751        base::Bind(DetailInputMatchesShippingField));
2752    FillOutputForSectionWithComparator(
2753        SECTION_CC,
2754        base::Bind(DetailInputMatchesShippingField));
2755    FillOutputForSectionWithComparator(
2756        SECTION_CC_BILLING,
2757        base::Bind(DetailInputMatchesShippingField));
2758  } else {
2759    FillOutputForSection(SECTION_SHIPPING);
2760  }
2761
2762  if (IsPayingWithWallet()) {
2763    profile_->GetPrefs()->SetBoolean(
2764        ::prefs::kAutofillDialogHasPaidWithWallet, true);
2765  } else {
2766    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2767      DialogSection section = static_cast<DialogSection>(i);
2768      if (!SectionIsActive(section))
2769        continue;
2770
2771      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2772      std::string item_key = model->GetItemKeyForCheckedItem();
2773      if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) {
2774        int variant = GetSelectedVariantForModel(*model);
2775        PersistAutofillChoice(section, item_key, variant);
2776      }
2777    }
2778  }
2779
2780  // On a successful submit, if the user manually selected "pay without wallet",
2781  // stop trying to pay with Wallet on future runs of the dialog. On the other
2782  // hand, if there was an error that prevented the user from having the choice
2783  // of using Wallet, leave the pref alone.
2784  if (!account_chooser_model_.had_wallet_error() &&
2785      account_chooser_model_.HasAccountsToChoose()) {
2786    profile_->GetPrefs()->SetBoolean(
2787        ::prefs::kAutofillDialogPayWithoutWallet,
2788        !account_chooser_model_.WalletIsSelected());
2789  }
2790
2791  if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT) {
2792    // Stop observing PersonalDataManager to avoid the dialog redrawing while
2793    // in an Autocheckout flow.
2794    GetManager()->RemoveObserver(this);
2795    autocheckout_started_timestamp_ = base::Time::Now();
2796    DCHECK_EQ(AUTOCHECKOUT_NOT_STARTED, autocheckout_state_);
2797    autocheckout_state_ = AUTOCHECKOUT_IN_PROGRESS;
2798    view_->UpdateButtonStrip();
2799    view_->UpdateDetailArea();
2800    view_->UpdateNotificationArea();
2801  }
2802
2803  LogOnFinishSubmitMetrics();
2804
2805  // Callback should be called as late as possible.
2806  callback_.Run(&form_structure_, !wallet_items_ ? std::string() :
2807      wallet_items_->google_transaction_id());
2808  callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
2809
2810  // This might delete us.
2811  if (GetDialogType() == DIALOG_TYPE_REQUEST_AUTOCOMPLETE)
2812    Hide();
2813}
2814
2815void AutofillDialogControllerImpl::PersistAutofillChoice(
2816    DialogSection section,
2817    const std::string& guid,
2818    int variant) {
2819  DCHECK(!IsPayingWithWallet());
2820  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
2821  value->SetString(kGuidPrefKey, guid);
2822  value->SetInteger(kVariantPrefKey, variant);
2823
2824  DictionaryPrefUpdate updater(profile()->GetPrefs(),
2825                               ::prefs::kAutofillDialogAutofillDefault);
2826  base::DictionaryValue* autofill_choice = updater.Get();
2827  autofill_choice->Set(SectionToPrefString(section), value.release());
2828}
2829
2830void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
2831    DialogSection section,
2832    std::string* guid,
2833    int* variant) {
2834  DCHECK(!IsPayingWithWallet());
2835  // The default choice is the first thing in the menu that is a suggestion
2836  // item.
2837  *variant = 0;
2838  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2839  for (int i = 0; i < model->GetItemCount(); ++i) {
2840    if (IsASuggestionItemKey(model->GetItemKeyAt(i))) {
2841      *guid = model->GetItemKeyAt(i);
2842      break;
2843    }
2844  }
2845}
2846
2847bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section,
2848                                                     std::string* guid,
2849                                                     int* variant) {
2850  DCHECK(!IsPayingWithWallet());
2851  const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary(
2852      ::prefs::kAutofillDialogAutofillDefault);
2853  if (!choices)
2854    return false;
2855
2856  const base::DictionaryValue* choice = NULL;
2857  if (!choices->GetDictionary(SectionToPrefString(section), &choice))
2858    return false;
2859
2860  choice->GetString(kGuidPrefKey, guid);
2861  choice->GetInteger(kVariantPrefKey, variant);
2862  return true;
2863}
2864
2865size_t AutofillDialogControllerImpl::GetSelectedVariantForModel(
2866    const SuggestionsMenuModel& model) {
2867  size_t variant = 0;
2868  // Calculate the variant by looking at how many items come from the same
2869  // data model.
2870  for (int i = model.checked_item() - 1; i >= 0; --i) {
2871    if (model.GetItemKeyAt(i) == model.GetItemKeyForCheckedItem())
2872      variant++;
2873    else
2874      break;
2875  }
2876  return variant;
2877}
2878
2879void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
2880  GetMetricLogger().LogDialogUiDuration(
2881      base::Time::Now() - dialog_shown_timestamp_,
2882      GetDialogType(),
2883      AutofillMetrics::DIALOG_ACCEPTED);
2884
2885  GetMetricLogger().LogDialogUiEvent(
2886      GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED);
2887
2888  AutofillMetrics::DialogDismissalState dismissal_state;
2889  if (!IsManuallyEditingAnySection())
2890    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_DATA;
2891  else if (IsPayingWithWallet())
2892    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_WALLET;
2893  else if (ShouldSaveDetailsLocally())
2894    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL;
2895  else
2896    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE;
2897
2898  GetMetricLogger().LogDialogDismissalState(GetDialogType(), dismissal_state);
2899}
2900
2901void AutofillDialogControllerImpl::LogOnCancelMetrics() {
2902  GetMetricLogger().LogDialogUiEvent(
2903      GetDialogType(), AutofillMetrics::DIALOG_UI_CANCELED);
2904
2905  AutofillMetrics::DialogDismissalState dismissal_state;
2906  if (!IsManuallyEditingAnySection())
2907    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS;
2908  else if (AllSectionsAreValid())
2909    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS;
2910  else
2911    dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS;
2912
2913  GetMetricLogger().LogDialogDismissalState(GetDialogType(), dismissal_state);
2914
2915  GetMetricLogger().LogDialogUiDuration(
2916      base::Time::Now() - dialog_shown_timestamp_,
2917      GetDialogType(),
2918      AutofillMetrics::DIALOG_CANCELED);
2919}
2920
2921void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
2922    const SuggestionsMenuModel& model) {
2923  DialogSection section = SectionForSuggestionsMenuModel(model);
2924
2925  AutofillMetrics::DialogUiEvent dialog_ui_event;
2926  if (model.GetItemKeyForCheckedItem() == kAddNewItemKey) {
2927    // Selected to add a new item.
2928    dialog_ui_event = DialogSectionToUiItemAddedEvent(section);
2929  } else if (IsASuggestionItemKey(model.GetItemKeyForCheckedItem())) {
2930    // Selected an existing item.
2931    dialog_ui_event = DialogSectionToUiSelectionChangedEvent(section);
2932  } else {
2933    // TODO(estade): add logging for "Manage items" or "Use billing for
2934    // shipping"?
2935    return;
2936  }
2937
2938  GetMetricLogger().LogDialogUiEvent(GetDialogType(), dialog_ui_event);
2939}
2940
2941void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
2942  if (was_ui_latency_logged_)
2943    return;
2944
2945  GetMetricLogger().LogDialogLatencyToShow(
2946      GetDialogType(),
2947      base::Time::Now() - dialog_shown_timestamp_);
2948  was_ui_latency_logged_ = true;
2949}
2950
2951AutofillMetrics::DialogInitialUserStateMetric
2952    AutofillDialogControllerImpl::GetInitialUserState() const {
2953  // Consider a user to be an Autofill user if the user has any credit cards
2954  // or addresses saved. Check that the item count is greater than 2 because
2955  // an "empty" menu still has the "add new" menu item and "manage" menu item.
2956  const bool has_autofill_profiles =
2957      suggested_cc_.GetItemCount() > 2 ||
2958      suggested_billing_.GetItemCount() > 2;
2959
2960  if (SignedInState() != SIGNED_IN) {
2961    // Not signed in.
2962    return has_autofill_profiles ?
2963        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL :
2964        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL;
2965  }
2966
2967  // Signed in.
2968  if (wallet_items_->instruments().empty()) {
2969    // No Wallet items.
2970    return has_autofill_profiles ?
2971        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL :
2972        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL;
2973  }
2974
2975  // Has Wallet items.
2976  return has_autofill_profiles ?
2977      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL :
2978      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL;
2979}
2980
2981}  // namespace autofill
2982