autofill_dialog_controller_impl.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
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_STATE:
1241      if (!value.empty() && !autofill::IsValidState(value))
1242        return ASCIIToUTF16("Are you sure this is right?");
1243      break;
1244
1245    case ADDRESS_HOME_CITY:
1246    case ADDRESS_HOME_ZIP:
1247    case ADDRESS_HOME_COUNTRY:
1248      break;
1249
1250    case NAME_FULL:  // Used for shipping.
1251      break;
1252
1253    case PHONE_HOME_WHOLE_NUMBER:  // Used in shipping section.
1254      break;
1255
1256    case PHONE_BILLING_WHOLE_NUMBER:  // Used in billing section.
1257      break;
1258
1259    default:
1260      NOTREACHED();  // Trying to validate unknown field.
1261      break;
1262  }
1263
1264  return value.empty() ?
1265      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE) :
1266      base::string16();
1267}
1268
1269// TODO(estade): Replace all the error messages here with more helpful and
1270// translateable ones. TODO(groby): Also add tests.
1271ValidityData AutofillDialogControllerImpl::InputsAreValid(
1272    const DetailOutputMap& inputs, ValidationType validation_type) const {
1273  ValidityData invalid_messages;
1274  std::map<AutofillFieldType, string16> field_values;
1275  for (DetailOutputMap::const_iterator iter = inputs.begin();
1276       iter != inputs.end(); ++iter) {
1277    // Skip empty fields in edit mode.
1278    if (validation_type == VALIDATE_EDIT && iter->second.empty())
1279      continue;
1280
1281    const AutofillFieldType type = iter->first->type;
1282    string16 message = InputValidityMessage(type, iter->second);
1283    if (!message.empty())
1284      invalid_messages[type] = message;
1285    else
1286      field_values[type] = iter->second;
1287  }
1288
1289  // Validate the date formed by month and year field. (Autofill dialog is
1290  // never supposed to have 2-digit years, so not checked).
1291  if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
1292      field_values.count(CREDIT_CARD_EXP_MONTH) &&
1293      !IsCreditCardExpirationValid(field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
1294                                   field_values[CREDIT_CARD_EXP_MONTH])) {
1295    // The dialog shows the same error message for the month and year fields.
1296    invalid_messages[CREDIT_CARD_EXP_4_DIGIT_YEAR] = l10n_util::GetStringUTF16(
1297        IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1298    invalid_messages[CREDIT_CARD_EXP_MONTH] = l10n_util::GetStringUTF16(
1299        IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1300  }
1301
1302  // If there is a credit card number and a CVC, validate them together.
1303  if (field_values.count(CREDIT_CARD_NUMBER) &&
1304      field_values.count(CREDIT_CARD_VERIFICATION_CODE) &&
1305      InputValidityMessage(CREDIT_CARD_NUMBER,
1306                           field_values[CREDIT_CARD_NUMBER]).empty() &&
1307      !autofill::IsValidCreditCardSecurityCode(
1308          field_values[CREDIT_CARD_VERIFICATION_CODE],
1309          field_values[CREDIT_CARD_NUMBER])) {
1310    invalid_messages[CREDIT_CARD_VERIFICATION_CODE] =
1311        l10n_util::GetStringUTF16(
1312            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1313  }
1314
1315  // Validate the shipping phone number against the country code of the address.
1316  if (field_values.count(ADDRESS_HOME_COUNTRY) &&
1317      field_values.count(PHONE_HOME_WHOLE_NUMBER)) {
1318    i18n::PhoneObject phone_object(
1319        field_values[PHONE_HOME_WHOLE_NUMBER],
1320        AutofillCountry::GetCountryCode(
1321            field_values[ADDRESS_HOME_COUNTRY],
1322            g_browser_process->GetApplicationLocale()));
1323    if (!phone_object.IsValidNumber()) {
1324      invalid_messages[PHONE_HOME_WHOLE_NUMBER] = l10n_util::GetStringUTF16(
1325          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1326    }
1327  }
1328
1329  // Validate the billing phone number against the country code of the address.
1330  if (field_values.count(ADDRESS_BILLING_COUNTRY) &&
1331      field_values.count(PHONE_BILLING_WHOLE_NUMBER)) {
1332    i18n::PhoneObject phone_object(
1333        field_values[PHONE_BILLING_WHOLE_NUMBER],
1334        AutofillCountry::GetCountryCode(
1335            field_values[ADDRESS_BILLING_COUNTRY],
1336            g_browser_process->GetApplicationLocale()));
1337    if (!phone_object.IsValidNumber()) {
1338      invalid_messages[PHONE_BILLING_WHOLE_NUMBER] = l10n_util::GetStringUTF16(
1339          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1340    }
1341  }
1342
1343  return invalid_messages;
1344}
1345
1346void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1347    const DetailInput* input,
1348    gfx::NativeView parent_view,
1349    const gfx::Rect& content_bounds,
1350    const string16& field_contents,
1351    bool was_edit) {
1352  // If the field is edited down to empty, don't show a popup.
1353  if (was_edit && field_contents.empty()) {
1354    HidePopup();
1355    return;
1356  }
1357
1358  // If the user clicks while the popup is already showing, be sure to hide
1359  // it.
1360  if (!was_edit && popup_controller_.get()) {
1361    HidePopup();
1362    return;
1363  }
1364
1365  std::vector<string16> popup_values, popup_labels, popup_icons;
1366  if (IsCreditCardType(input->type)) {
1367    GetManager()->GetCreditCardSuggestions(input->type,
1368                                           field_contents,
1369                                           &popup_values,
1370                                           &popup_labels,
1371                                           &popup_icons,
1372                                           &popup_guids_);
1373  } else {
1374    std::vector<AutofillFieldType> field_types;
1375    field_types.push_back(EMAIL_ADDRESS);
1376    for (DetailInputs::const_iterator iter = requested_shipping_fields_.begin();
1377         iter != requested_shipping_fields_.end(); ++iter) {
1378      field_types.push_back(iter->type);
1379    }
1380    GetManager()->GetProfileSuggestions(input->type,
1381                                        field_contents,
1382                                        false,
1383                                        field_types,
1384                                        &popup_values,
1385                                        &popup_labels,
1386                                        &popup_icons,
1387                                        &popup_guids_);
1388  }
1389
1390  if (popup_values.empty()) {
1391    HidePopup();
1392    return;
1393  }
1394
1395  // TODO(estade): do we need separators and control rows like 'Clear
1396  // Form'?
1397  std::vector<int> popup_ids;
1398  for (size_t i = 0; i < popup_guids_.size(); ++i) {
1399    popup_ids.push_back(i);
1400  }
1401
1402  popup_controller_ = AutofillPopupControllerImpl::GetOrCreate(
1403      popup_controller_,
1404      weak_ptr_factory_.GetWeakPtr(),
1405      parent_view,
1406      content_bounds);
1407  popup_controller_->Show(popup_values,
1408                          popup_labels,
1409                          popup_icons,
1410                          popup_ids);
1411  input_showing_popup_ = input;
1412}
1413
1414void AutofillDialogControllerImpl::FocusMoved() {
1415  HidePopup();
1416}
1417
1418void AutofillDialogControllerImpl::ViewClosed() {
1419  GetManager()->RemoveObserver(this);
1420
1421  // TODO(ahutter): Once a user can cancel Autocheckout mid-flow, log that
1422  // metric here.
1423
1424  delete this;
1425}
1426
1427std::vector<DialogNotification> AutofillDialogControllerImpl::
1428    CurrentNotifications() {
1429  std::vector<DialogNotification> notifications;
1430
1431  if (account_chooser_model_.had_wallet_error()) {
1432    // TODO(dbeam): pass along the Wallet error or remove from the translation.
1433    // TODO(dbeam): figure out a way to dismiss this error after a while.
1434    notifications.push_back(DialogNotification(
1435        DialogNotification::WALLET_ERROR,
1436        l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_COMPLETE_WITHOUT_WALLET,
1437                                   ASCIIToUTF16("[Wallet-Error]."))));
1438  } else if (should_show_wallet_promo_) {
1439    if (IsPayingWithWallet() && HasCompleteWallet()) {
1440      notifications.push_back(DialogNotification(
1441          DialogNotification::EXPLANATORY_MESSAGE,
1442          l10n_util::GetStringUTF16(
1443              IDS_AUTOFILL_DIALOG_DETAILS_FROM_WALLET)));
1444    } else if ((IsPayingWithWallet() && !HasCompleteWallet()) ||
1445               has_shown_wallet_usage_confirmation_) {
1446      DialogNotification notification(
1447          DialogNotification::WALLET_USAGE_CONFIRMATION,
1448          l10n_util::GetStringUTF16(
1449              IDS_AUTOFILL_DIALOG_SAVE_DETAILS_IN_WALLET));
1450      notification.set_checked(account_chooser_model_.WalletIsSelected());
1451      notification.set_interactive(!is_submitting_);
1452      notifications.push_back(notification);
1453      has_shown_wallet_usage_confirmation_ = true;
1454    }
1455  }
1456
1457  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
1458    notifications.push_back(DialogNotification(
1459        DialogNotification::SECURITY_WARNING,
1460        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECURITY_WARNING)));
1461  }
1462
1463  if (!invoked_from_same_origin_) {
1464    notifications.push_back(DialogNotification(
1465        DialogNotification::SECURITY_WARNING,
1466        l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING,
1467                                   UTF8ToUTF16(source_url_.host()))));
1468  }
1469
1470  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1471    notifications.push_back(DialogNotification(
1472        DialogNotification::REQUIRED_ACTION,
1473        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VERIFY_CVV)));
1474  }
1475
1476  if (autocheckout_state_ == AUTOCHECKOUT_ERROR) {
1477    notifications.push_back(DialogNotification(
1478        DialogNotification::AUTOCHECKOUT_ERROR,
1479        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_ERROR)));
1480  }
1481
1482  if (autocheckout_state_ == AUTOCHECKOUT_SUCCESS) {
1483    notifications.push_back(DialogNotification(
1484        DialogNotification::AUTOCHECKOUT_SUCCESS,
1485        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_AUTOCHECKOUT_SUCCESS)));
1486  }
1487
1488  if (wallet_server_validation_error_) {
1489    // TODO(ahutter): L10n and UI.
1490    notifications.push_back(DialogNotification(
1491        DialogNotification::REQUIRED_ACTION,
1492        ASCIIToUTF16("New data failed validation on server side")));
1493  }
1494
1495  if (IsPayingWithWallet() && !wallet::IsUsingProd()) {
1496    notifications.push_back(DialogNotification(
1497        DialogNotification::DEVELOPER_WARNING,
1498        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_NOT_PROD_WARNING)));
1499  }
1500
1501  if (choose_another_instrument_or_address_) {
1502    notifications.push_back(DialogNotification(
1503        DialogNotification::REQUIRED_ACTION,
1504        ASCIIToUTF16("We need more information to complete your purchase.")));
1505  }
1506
1507  return notifications;
1508}
1509
1510void AutofillDialogControllerImpl::SignInLinkClicked() {
1511  if (signin_registrar_.IsEmpty()) {
1512    // Start sign in.
1513    DCHECK(!IsPayingWithWallet());
1514
1515    content::Source<content::NavigationController> source(view_->ShowSignIn());
1516    signin_registrar_.Add(
1517        this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
1518    view_->UpdateAccountChooser();
1519
1520    GetMetricLogger().LogDialogUiEvent(
1521        GetDialogType(), AutofillMetrics::DIALOG_UI_SIGNIN_SHOWN);
1522  } else {
1523    HideSignIn();
1524  }
1525}
1526
1527void AutofillDialogControllerImpl::NotificationCheckboxStateChanged(
1528    DialogNotification::Type type, bool checked) {
1529  if (type == DialogNotification::WALLET_USAGE_CONFIRMATION) {
1530    if (checked)
1531      account_chooser_model_.SelectActiveWalletAccount();
1532    else
1533      account_chooser_model_.SelectUseAutofill();
1534  }
1535}
1536
1537void AutofillDialogControllerImpl::LegalDocumentLinkClicked(
1538    const ui::Range& range) {
1539  for (size_t i = 0; i < legal_document_link_ranges_.size(); ++i) {
1540    if (legal_document_link_ranges_[i] == range) {
1541      OpenTabWithUrl(wallet_items_->legal_documents()[i]->url());
1542      return;
1543    }
1544  }
1545
1546  NOTREACHED();
1547}
1548
1549void AutofillDialogControllerImpl::OnCancel() {
1550  HidePopup();
1551
1552  // If the submit was successful, |callback_| will have already been |.Run()|
1553  // and nullified. If this is the case, no further actions are required. If
1554  // Autocheckout has an error, it's possible that the dialog will be submitted
1555  // to start the flow and then cancelled to close the dialog after the error.
1556  if (callback_.is_null())
1557    return;
1558
1559  LogOnCancelMetrics();
1560
1561  callback_.Run(NULL, std::string());
1562  callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
1563}
1564
1565void AutofillDialogControllerImpl::OnAccept() {
1566  choose_another_instrument_or_address_ = false;
1567  HidePopup();
1568  SetIsSubmitting(true);
1569  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1570    DCHECK(!active_instrument_id_.empty());
1571    GetWalletClient()->AuthenticateInstrument(
1572        active_instrument_id_,
1573        UTF16ToUTF8(view_->GetCvc()),
1574        wallet_items_->obfuscated_gaia_id());
1575  } else if (IsPayingWithWallet()) {
1576    SubmitWithWallet();
1577  } else {
1578    FinishSubmit();
1579  }
1580}
1581
1582Profile* AutofillDialogControllerImpl::profile() {
1583  return profile_;
1584}
1585
1586content::WebContents* AutofillDialogControllerImpl::web_contents() {
1587  return contents_;
1588}
1589
1590////////////////////////////////////////////////////////////////////////////////
1591// AutofillPopupDelegate implementation.
1592
1593void AutofillDialogControllerImpl::OnPopupShown(
1594    content::KeyboardListener* listener) {
1595  GetMetricLogger().LogDialogPopupEvent(
1596      GetDialogType(), AutofillMetrics::DIALOG_POPUP_SHOWN);
1597}
1598
1599void AutofillDialogControllerImpl::OnPopupHidden(
1600    content::KeyboardListener* listener) {}
1601
1602void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier) {
1603  // TODO(estade): implement.
1604}
1605
1606void AutofillDialogControllerImpl::DidAcceptSuggestion(const string16& value,
1607                                                       int identifier) {
1608  const PersonalDataManager::GUIDPair& pair = popup_guids_[identifier];
1609
1610  scoped_ptr<DataModelWrapper> wrapper;
1611  if (IsCreditCardType(input_showing_popup_->type)) {
1612    wrapper.reset(new AutofillCreditCardWrapper(
1613        GetManager()->GetCreditCardByGUID(pair.first)));
1614  } else {
1615    wrapper.reset(new AutofillProfileWrapper(
1616        GetManager()->GetProfileByGUID(pair.first), pair.second));
1617  }
1618
1619  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1620    DialogSection section = static_cast<DialogSection>(i);
1621    wrapper->FillInputs(MutableRequestedFieldsForSection(section));
1622    view_->FillSection(section, *input_showing_popup_);
1623  }
1624
1625  GetMetricLogger().LogDialogPopupEvent(
1626      GetDialogType(), AutofillMetrics::DIALOG_POPUP_FORM_FILLED);
1627
1628  // TODO(estade): not sure why it's necessary to do this explicitly.
1629  HidePopup();
1630}
1631
1632void AutofillDialogControllerImpl::RemoveSuggestion(const string16& value,
1633                                                    int identifier) {
1634  // TODO(estade): implement.
1635}
1636
1637void AutofillDialogControllerImpl::ClearPreviewedForm() {
1638  // TODO(estade): implement.
1639}
1640
1641////////////////////////////////////////////////////////////////////////////////
1642// content::NotificationObserver implementation.
1643
1644void AutofillDialogControllerImpl::Observe(
1645    int type,
1646    const content::NotificationSource& source,
1647    const content::NotificationDetails& details) {
1648  DCHECK_EQ(type, content::NOTIFICATION_NAV_ENTRY_COMMITTED);
1649  content::LoadCommittedDetails* load_details =
1650      content::Details<content::LoadCommittedDetails>(details).ptr();
1651  if (wallet::IsSignInContinueUrl(load_details->entry->GetVirtualURL())) {
1652    should_show_wallet_promo_ = false;
1653    HideSignIn();
1654    account_chooser_model_.SelectActiveWalletAccount();
1655    GetWalletItems();
1656  }
1657}
1658
1659////////////////////////////////////////////////////////////////////////////////
1660// SuggestionsMenuModelDelegate implementation.
1661
1662void AutofillDialogControllerImpl::SuggestionItemSelected(
1663    SuggestionsMenuModel* model,
1664    size_t index) {
1665  if (model->GetItemKeyAt(index) == kManageItemsKey) {
1666    GURL url;
1667    if (!IsPayingWithWallet()) {
1668      GURL settings_url(chrome::kChromeUISettingsURL);
1669      url = settings_url.Resolve(chrome::kAutofillSubPage);
1670    } else {
1671      url = SectionForSuggestionsMenuModel(*model) == SECTION_SHIPPING ?
1672          wallet::GetManageAddressesUrl() : wallet::GetManageInstrumentsUrl();
1673    }
1674
1675    OpenTabWithUrl(url);
1676    return;
1677  }
1678
1679  model->SetCheckedIndex(index);
1680  PrepareDetailInputsForSection(SectionForSuggestionsMenuModel(*model));
1681
1682  LogSuggestionItemSelectedMetric(*model);
1683}
1684
1685////////////////////////////////////////////////////////////////////////////////
1686// wallet::WalletClientDelegate implementation.
1687
1688const AutofillMetrics& AutofillDialogControllerImpl::GetMetricLogger() const {
1689  return metric_logger_;
1690}
1691
1692DialogType AutofillDialogControllerImpl::GetDialogType() const {
1693  return dialog_type_;
1694}
1695
1696std::string AutofillDialogControllerImpl::GetRiskData() const {
1697  return risk_data_.empty() ? "no pagers" : risk_data_;
1698}
1699
1700void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
1701  DCHECK(is_submitting_ && IsPayingWithWallet());
1702
1703  has_accepted_legal_documents_ = true;
1704  LoadRiskFingerprintData();
1705}
1706
1707void AutofillDialogControllerImpl::OnDidAuthenticateInstrument(bool success) {
1708  DCHECK(is_submitting_ && IsPayingWithWallet());
1709
1710  // TODO(dbeam): use the returned full wallet. b/8332329
1711  if (success)
1712    GetFullWallet();
1713  else
1714    DisableWallet();
1715}
1716
1717void AutofillDialogControllerImpl::OnDidGetFullWallet(
1718    scoped_ptr<wallet::FullWallet> full_wallet) {
1719  DCHECK(is_submitting_ && IsPayingWithWallet());
1720
1721  full_wallet_ = full_wallet.Pass();
1722
1723  if (full_wallet_->required_actions().empty()) {
1724    FinishSubmit();
1725    return;
1726  }
1727
1728  switch (full_wallet_->required_actions()[0]) {
1729    case wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS:
1730      choose_another_instrument_or_address_ = true;
1731      SetIsSubmitting(false);
1732      view_->UpdateNotificationArea();
1733      view_->UpdateButtonStrip();
1734      GetWalletItems();
1735      break;
1736
1737    case wallet::VERIFY_CVV:
1738      SuggestionsUpdated();
1739      view_->UpdateNotificationArea();
1740      view_->UpdateButtonStrip();
1741      break;
1742
1743    default:
1744      DisableWallet();
1745      break;
1746  }
1747}
1748
1749void AutofillDialogControllerImpl::OnPassiveSigninSuccess(
1750    const std::string& username) {
1751  const string16 username16 = UTF8ToUTF16(username);
1752  signin_helper_.reset();
1753  account_chooser_model_.SetActiveWalletAccountName(username16);
1754  GetWalletItems();
1755}
1756
1757void AutofillDialogControllerImpl::OnUserNameFetchSuccess(
1758    const std::string& username) {
1759  const string16 username16 = UTF8ToUTF16(username);
1760  signin_helper_.reset();
1761  account_chooser_model_.SetActiveWalletAccountName(username16);
1762  OnWalletOrSigninUpdate();
1763}
1764
1765void AutofillDialogControllerImpl::OnAutomaticSigninSuccess(
1766    const std::string& username) {
1767  NOTIMPLEMENTED();
1768}
1769
1770void AutofillDialogControllerImpl::OnPassiveSigninFailure(
1771    const GoogleServiceAuthError& error) {
1772  // TODO(aruslan): report an error.
1773  LOG(ERROR) << "failed to passively sign in: " << error.ToString();
1774  OnWalletSigninError();
1775}
1776
1777void AutofillDialogControllerImpl::OnUserNameFetchFailure(
1778    const GoogleServiceAuthError& error) {
1779  // TODO(aruslan): report an error.
1780  LOG(ERROR) << "failed to fetch the user account name: " << error.ToString();
1781  OnWalletSigninError();
1782}
1783
1784void AutofillDialogControllerImpl::OnAutomaticSigninFailure(
1785    const GoogleServiceAuthError& error) {
1786  // TODO(aruslan): report an error.
1787  LOG(ERROR) << "failed to automatically sign in: " << error.ToString();
1788  OnWalletSigninError();
1789}
1790
1791void AutofillDialogControllerImpl::OnDidGetWalletItems(
1792    scoped_ptr<wallet::WalletItems> wallet_items) {
1793  legal_documents_text_.clear();
1794  legal_document_link_ranges_.clear();
1795  has_accepted_legal_documents_ = false;
1796
1797  wallet_items_ = wallet_items.Pass();
1798  OnWalletOrSigninUpdate();
1799}
1800
1801void AutofillDialogControllerImpl::OnDidSaveAddress(
1802    const std::string& address_id,
1803    const std::vector<wallet::RequiredAction>& required_actions) {
1804  DCHECK(is_submitting_ && IsPayingWithWallet());
1805
1806  if (required_actions.empty()) {
1807    active_address_id_ = address_id;
1808    GetFullWalletIfReady();
1809  } else {
1810    HandleSaveOrUpdateRequiredActions(required_actions);
1811  }
1812}
1813
1814void AutofillDialogControllerImpl::OnDidSaveInstrument(
1815    const std::string& instrument_id,
1816    const std::vector<wallet::RequiredAction>& required_actions) {
1817  DCHECK(is_submitting_ && IsPayingWithWallet());
1818
1819  if (required_actions.empty()) {
1820    active_instrument_id_ = instrument_id;
1821    GetFullWalletIfReady();
1822  } else {
1823    HandleSaveOrUpdateRequiredActions(required_actions);
1824  }
1825}
1826
1827void AutofillDialogControllerImpl::OnDidSaveInstrumentAndAddress(
1828    const std::string& instrument_id,
1829    const std::string& address_id,
1830    const std::vector<wallet::RequiredAction>& required_actions) {
1831  OnDidSaveInstrument(instrument_id, required_actions);
1832  // |is_submitting_| can change while in |OnDidSaveInstrument()|.
1833  if (is_submitting_)
1834    OnDidSaveAddress(address_id, required_actions);
1835}
1836
1837void AutofillDialogControllerImpl::OnDidUpdateAddress(
1838    const std::string& address_id,
1839    const std::vector<wallet::RequiredAction>& required_actions) {
1840  OnDidSaveAddress(address_id, required_actions);
1841}
1842
1843void AutofillDialogControllerImpl::OnDidUpdateInstrument(
1844    const std::string& instrument_id,
1845    const std::vector<wallet::RequiredAction>& required_actions) {
1846  OnDidSaveInstrument(instrument_id, required_actions);
1847}
1848
1849void AutofillDialogControllerImpl::OnWalletError(
1850    wallet::WalletClient::ErrorType error_type) {
1851  // TODO(dbeam): Do something with |error_type|. http://crbug.com/164410
1852  DisableWallet();
1853}
1854
1855void AutofillDialogControllerImpl::OnMalformedResponse() {
1856  DisableWallet();
1857}
1858
1859void AutofillDialogControllerImpl::OnNetworkError(int response_code) {
1860  DisableWallet();
1861}
1862
1863////////////////////////////////////////////////////////////////////////////////
1864// PersonalDataManagerObserver implementation.
1865
1866void AutofillDialogControllerImpl::OnPersonalDataChanged() {
1867  if (is_submitting_)
1868    return;
1869
1870  SuggestionsUpdated();
1871}
1872
1873////////////////////////////////////////////////////////////////////////////////
1874// AccountChooserModelDelegate implementation.
1875
1876void AutofillDialogControllerImpl::AccountChoiceChanged() {
1877  if (is_submitting_)
1878    GetWalletClient()->CancelRequests();
1879
1880  SetIsSubmitting(false);
1881
1882  SuggestionsUpdated();
1883  UpdateAccountChooserView();
1884}
1885
1886void AutofillDialogControllerImpl::UpdateAccountChooserView() {
1887  if (view_) {
1888    view_->UpdateAccountChooser();
1889    view_->UpdateNotificationArea();
1890  }
1891}
1892
1893////////////////////////////////////////////////////////////////////////////////
1894
1895bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
1896    const content::NativeWebKeyboardEvent& event) {
1897  if (popup_controller_.get())
1898    return popup_controller_->HandleKeyPressEvent(event);
1899
1900  return false;
1901}
1902
1903bool AutofillDialogControllerImpl::RequestingCreditCardInfo() const {
1904  DCHECK_GT(form_structure_.field_count(), 0U);
1905
1906  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1907    if (IsCreditCardType(form_structure_.field(i)->type()))
1908      return true;
1909  }
1910
1911  return false;
1912}
1913
1914bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
1915  return source_url_.SchemeIs(chrome::kHttpsScheme) &&
1916         !net::IsCertStatusError(ssl_status_.cert_status) &&
1917         !net::IsCertStatusMinorError(ssl_status_.cert_status);
1918}
1919
1920AutofillDialogControllerImpl::AutofillDialogControllerImpl(
1921    content::WebContents* contents,
1922    const FormData& form_structure,
1923    const GURL& source_url,
1924    const DialogType dialog_type,
1925    const base::Callback<void(const FormStructure*,
1926                              const std::string&)>& callback)
1927    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
1928      contents_(contents),
1929      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
1930      dialog_type_(dialog_type),
1931      form_structure_(form_structure, std::string()),
1932      invoked_from_same_origin_(true),
1933      source_url_(source_url),
1934      ssl_status_(form_structure.ssl_status),
1935      callback_(callback),
1936      account_chooser_model_(this, profile_->GetPrefs(), metric_logger_,
1937                             dialog_type),
1938      wallet_client_(profile_->GetRequestContext(), this),
1939      suggested_email_(this),
1940      suggested_cc_(this),
1941      suggested_billing_(this),
1942      suggested_cc_billing_(this),
1943      suggested_shipping_(this),
1944      cares_about_shipping_(true),
1945      input_showing_popup_(NULL),
1946      weak_ptr_factory_(this),
1947      should_show_wallet_promo_(!profile_->GetPrefs()->GetBoolean(
1948          ::prefs::kAutofillDialogHasPaidWithWallet)),
1949      has_shown_wallet_usage_confirmation_(false),
1950      has_accepted_legal_documents_(false),
1951      is_submitting_(false),
1952      wallet_server_validation_error_(false),
1953      choose_another_instrument_or_address_(false),
1954      autocheckout_state_(AUTOCHECKOUT_NOT_STARTED),
1955      was_ui_latency_logged_(false) {
1956  // TODO(estade): remove duplicates from |form_structure|?
1957  DCHECK(!callback_.is_null());
1958}
1959
1960AutofillDialogView* AutofillDialogControllerImpl::CreateView() {
1961  return AutofillDialogView::Create(this);
1962}
1963
1964PersonalDataManager* AutofillDialogControllerImpl::GetManager() {
1965  return PersonalDataManagerFactory::GetForProfile(profile_);
1966}
1967
1968wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient() {
1969  return &wallet_client_;
1970}
1971
1972bool AutofillDialogControllerImpl::IsPayingWithWallet() const {
1973  return account_chooser_model_.WalletIsSelected() &&
1974         SignedInState() == SIGNED_IN;
1975}
1976
1977void AutofillDialogControllerImpl::LoadRiskFingerprintData() {
1978  DCHECK(AreLegalDocumentsCurrent());
1979
1980  // Clear potential stale data to ensure |GetFullWalletIfReady()| triggers only
1981  // when a new fingerprint is loaded.
1982  risk_data_.clear();
1983
1984  uint64 obfuscated_gaia_id = 0;
1985  bool success = base::StringToUint64(wallet_items_->obfuscated_gaia_id(),
1986                                      &obfuscated_gaia_id);
1987  DCHECK(success);
1988
1989  gfx::Rect window_bounds;
1990#if !defined(OS_ANDROID)
1991  window_bounds = GetBaseWindowForWebContents(web_contents())->GetBounds();
1992#else
1993  // TODO(dbeam): figure out the correct browser window size to pass along for
1994  // android.
1995#endif
1996
1997  PrefService* user_prefs = profile_->GetPrefs();
1998  std::string charset = user_prefs->GetString(::prefs::kDefaultCharset);
1999  std::string accept_languages =
2000      user_prefs->GetString(::prefs::kAcceptLanguages);
2001  base::Time install_time = base::Time::FromTimeT(
2002      g_browser_process->local_state()->GetInt64(::prefs::kInstallDate));
2003
2004  risk::GetFingerprint(
2005      obfuscated_gaia_id, window_bounds, *web_contents(),
2006      chrome::VersionInfo().Version(), charset, accept_languages, install_time,
2007      dialog_type_, g_browser_process->GetApplicationLocale(),
2008      base::Bind(&AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData,
2009                 weak_ptr_factory_.GetWeakPtr()));
2010}
2011
2012void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
2013    scoped_ptr<risk::Fingerprint> fingerprint) {
2014  DCHECK(AreLegalDocumentsCurrent());
2015
2016  std::string proto_data;
2017  fingerprint->SerializeToString(&proto_data);
2018  bool success = base::Base64Encode(proto_data, &risk_data_);
2019  DCHECK(success);
2020
2021  GetFullWalletIfReady();
2022}
2023
2024void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL& url) {
2025#if !defined(OS_ANDROID)
2026  chrome::NavigateParams params(
2027      chrome::FindBrowserWithWebContents(web_contents()),
2028      url,
2029      content::PAGE_TRANSITION_AUTO_BOOKMARK);
2030  params.disposition = NEW_FOREGROUND_TAB;
2031  chrome::Navigate(&params);
2032#else
2033  // TODO(estade): use TabModelList?
2034#endif
2035}
2036
2037void AutofillDialogControllerImpl::OnWalletSigninError() {
2038  signin_helper_.reset();
2039  account_chooser_model_.SetHadWalletSigninError();
2040  GetWalletClient()->CancelRequests();
2041  LogDialogLatencyToShow();
2042}
2043
2044void AutofillDialogControllerImpl::DisableWallet() {
2045  signin_helper_.reset();
2046  wallet_items_.reset();
2047  GetWalletClient()->CancelRequests();
2048  SetIsSubmitting(false);
2049  account_chooser_model_.SetHadWalletError();
2050}
2051
2052void AutofillDialogControllerImpl::SuggestionsUpdated() {
2053  suggested_email_.Reset();
2054  suggested_cc_.Reset();
2055  suggested_billing_.Reset();
2056  suggested_cc_billing_.Reset();
2057  suggested_shipping_.Reset();
2058  HidePopup();
2059
2060  suggested_shipping_.AddKeyedItem(
2061      kSameAsBillingKey,
2062      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING));
2063
2064  if (IsPayingWithWallet()) {
2065    if (!account_chooser_model_.active_wallet_account_name().empty()) {
2066      suggested_email_.AddKeyedItem(
2067          base::IntToString(0),
2068          account_chooser_model_.active_wallet_account_name());
2069    }
2070
2071    const std::vector<wallet::Address*>& addresses =
2072        wallet_items_->addresses();
2073    for (size_t i = 0; i < addresses.size(); ++i) {
2074      std::string key = base::IntToString(i);
2075      suggested_shipping_.AddKeyedItemWithSublabel(
2076          key,
2077          addresses[i]->DisplayName(),
2078          addresses[i]->DisplayNameDetail());
2079
2080      if (addresses[i]->object_id() == wallet_items_->default_address_id())
2081        suggested_shipping_.SetCheckedItem(key);
2082    }
2083
2084    if (!IsSubmitPausedOn(wallet::VERIFY_CVV)) {
2085      const std::vector<wallet::WalletItems::MaskedInstrument*>& instruments =
2086          wallet_items_->instruments();
2087      std::string first_active_instrument_key;
2088      std::string default_instrument_key;
2089      for (size_t i = 0; i < instruments.size(); ++i) {
2090        bool allowed = IsInstrumentAllowed(*instruments[i]);
2091        gfx::Image icon = instruments[i]->CardIcon();
2092        if (!allowed && !icon.IsEmpty()) {
2093          // Create a grayed disabled icon.
2094          SkBitmap disabled_bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
2095              *icon.ToSkBitmap(), kGrayImageShift);
2096          icon = gfx::Image(
2097              gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap));
2098        }
2099        std::string key = base::IntToString(i);
2100        suggested_cc_billing_.AddKeyedItemWithSublabelAndIcon(
2101            key,
2102            instruments[i]->DisplayName(),
2103            instruments[i]->DisplayNameDetail(),
2104            icon);
2105        suggested_cc_billing_.SetEnabled(key, allowed);
2106
2107        if (allowed) {
2108          if (first_active_instrument_key.empty())
2109            first_active_instrument_key = key;
2110          if (instruments[i]->object_id() ==
2111              wallet_items_->default_instrument_id()) {
2112            default_instrument_key = key;
2113          }
2114        }
2115      }
2116
2117      // TODO(estade): this should have a URL sublabel.
2118      suggested_cc_billing_.AddKeyedItem(
2119          kAddNewItemKey,
2120          l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_DETAILS));
2121      if (!wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2122        suggested_cc_billing_.AddKeyedItem(
2123            kManageItemsKey,
2124            l10n_util::GetStringUTF16(
2125                IDS_AUTOFILL_DIALOG_MANAGE_BILLING_DETAILS));
2126      }
2127
2128      // Determine which instrument item should be selected.
2129      if (!default_instrument_key.empty())
2130        suggested_cc_billing_.SetCheckedItem(default_instrument_key);
2131      else if (!first_active_instrument_key.empty())
2132        suggested_cc_billing_.SetCheckedItem(first_active_instrument_key);
2133      else
2134        suggested_cc_billing_.SetCheckedItem(kAddNewItemKey);
2135    }
2136  } else {
2137    PersonalDataManager* manager = GetManager();
2138    const std::vector<CreditCard*>& cards = manager->GetCreditCards();
2139    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
2140    for (size_t i = 0; i < cards.size(); ++i) {
2141      if (!HasCompleteAndVerifiedData(*cards[i], requested_cc_fields_))
2142        continue;
2143
2144      suggested_cc_.AddKeyedItemWithIcon(
2145          cards[i]->guid(),
2146          cards[i]->Label(),
2147          rb.GetImageNamed(cards[i]->IconResourceId()));
2148    }
2149
2150    const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
2151    const std::string app_locale = g_browser_process->GetApplicationLocale();
2152    for (size_t i = 0; i < profiles.size(); ++i) {
2153      if (!HasCompleteAndVerifiedData(*profiles[i], requested_shipping_fields_))
2154        continue;
2155
2156      // Add all email addresses.
2157      std::vector<string16> values;
2158      profiles[i]->GetMultiInfo(EMAIL_ADDRESS, app_locale, &values);
2159      for (size_t j = 0; j < values.size(); ++j) {
2160        if (!values[j].empty())
2161          suggested_email_.AddKeyedItem(profiles[i]->guid(), values[j]);
2162      }
2163
2164      // Don't add variants for addresses: the email variants are handled above,
2165      // name is part of credit card and we'll just ignore phone number
2166      // variants.
2167      suggested_billing_.AddKeyedItem(profiles[i]->guid(),
2168                                      profiles[i]->Label());
2169      suggested_shipping_.AddKeyedItem(profiles[i]->guid(),
2170                                       profiles[i]->Label());
2171    }
2172
2173    suggested_cc_.AddKeyedItem(
2174        kAddNewItemKey,
2175        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD));
2176    suggested_cc_.AddKeyedItem(
2177        kManageItemsKey,
2178        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD));
2179    suggested_billing_.AddKeyedItem(
2180        kAddNewItemKey,
2181        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS));
2182    suggested_billing_.AddKeyedItem(
2183        kManageItemsKey,
2184        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS));
2185  }
2186
2187  suggested_email_.AddKeyedItem(
2188      kAddNewItemKey,
2189      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_EMAIL_ADDRESS));
2190  if (!IsPayingWithWallet()) {
2191    suggested_email_.AddKeyedItem(
2192        kManageItemsKey,
2193        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_EMAIL_ADDRESS));
2194  }
2195
2196  suggested_shipping_.AddKeyedItem(
2197      kAddNewItemKey,
2198      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS));
2199  if (!IsPayingWithWallet() ||
2200      !wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2201    suggested_shipping_.AddKeyedItem(
2202        kManageItemsKey,
2203        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS));
2204  }
2205
2206  if (!IsPayingWithWallet()) {
2207    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2208      DialogSection section = static_cast<DialogSection>(i);
2209      if (!SectionIsActive(section))
2210        continue;
2211
2212      // Set the starting choice for the menu. First set to the default in case
2213      // the GUID saved in prefs refers to a profile that no longer exists.
2214      std::string guid;
2215      int variant;
2216      GetDefaultAutofillChoice(section, &guid, &variant);
2217      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2218      model->SetCheckedItemNthWithKey(guid, variant + 1);
2219      if (GetAutofillChoice(section, &guid, &variant))
2220        model->SetCheckedItemNthWithKey(guid, variant + 1);
2221    }
2222  }
2223
2224  if (view_)
2225    view_->ModelChanged();
2226
2227  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2228    PrepareDetailInputsForSection(static_cast<DialogSection>(section));
2229  }
2230}
2231
2232void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
2233    DialogSection section,
2234    const InputFieldComparator& compare) {
2235  const DetailInputs& inputs = RequestedFieldsForSection(section);
2236
2237  // Email is hidden while using Wallet, special case it.
2238  if (section == SECTION_EMAIL && IsPayingWithWallet()) {
2239    AutofillProfile profile;
2240    profile.SetRawInfo(EMAIL_ADDRESS,
2241                       account_chooser_model_.active_wallet_account_name());
2242    AutofillProfileWrapper profile_wrapper(&profile, 0);
2243    profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2244    return;
2245  }
2246
2247  if (!SectionIsActive(section))
2248    return;
2249
2250  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2251  if (wrapper) {
2252    // Only fill in data that is associated with this section.
2253    const DetailInputs& inputs = RequestedFieldsForSection(section);
2254    wrapper->FillFormStructure(inputs, compare, &form_structure_);
2255
2256    // CVC needs special-casing because the CreditCard class doesn't store or
2257    // handle them. This isn't necessary when filling the combined CC and
2258    // billing section as CVC comes from |full_wallet_| in this case.
2259    if (section == SECTION_CC)
2260      SetCvcResult(view_->GetCvc());
2261  } else {
2262    // The user manually input data. If using Autofill, save the info as new or
2263    // edited data. Always fill local data into |form_structure_|.
2264    DetailOutputMap output;
2265    view_->GetUserInput(section, &output);
2266
2267    if (section == SECTION_CC) {
2268      CreditCard card;
2269      card.set_origin(kAutofillDialogOrigin);
2270      FillFormGroupFromOutputs(output, &card);
2271
2272      if (ShouldSaveDetailsLocally())
2273        GetManager()->SaveImportedCreditCard(card);
2274
2275      AutofillCreditCardWrapper card_wrapper(&card);
2276      card_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2277
2278      // Again, CVC needs special-casing. Fill it in directly from |output|.
2279      SetCvcResult(GetValueForType(output, CREDIT_CARD_VERIFICATION_CODE));
2280    } else {
2281      AutofillProfile profile;
2282      profile.set_origin(kAutofillDialogOrigin);
2283      FillFormGroupFromOutputs(output, &profile);
2284
2285      // For billing, the profile name has to come from the CC section.
2286      if (section == SECTION_BILLING) {
2287        profile.SetRawInfo(NAME_FULL,
2288                           GetValueFromSection(SECTION_CC, CREDIT_CARD_NAME));
2289        profile.SetRawInfo(EMAIL_ADDRESS,
2290                           GetValueFromSection(SECTION_EMAIL, EMAIL_ADDRESS));
2291      }
2292
2293      if (ShouldSaveDetailsLocally())
2294        SaveProfileGleanedFromSection(profile, section);
2295
2296      AutofillProfileWrapper profile_wrapper(&profile, 0);
2297      profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2298    }
2299  }
2300}
2301
2302void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) {
2303  FillOutputForSectionWithComparator(section,
2304                                     base::Bind(DetailInputMatchesField));
2305}
2306
2307bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
2308    DialogSection section) const {
2309  // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
2310  // any of the fields.
2311  // TODO(estade): remove !IsPayingWithWallet() check once WalletClient support
2312  // is added. http://crbug.com/243514
2313  if (section == SECTION_SHIPPING && !IsPayingWithWallet())
2314    return cares_about_shipping_;
2315
2316  return true;
2317}
2318
2319void AutofillDialogControllerImpl::SetCvcResult(const string16& cvc) {
2320  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
2321    AutofillField* field = form_structure_.field(i);
2322    if (field->type() == CREDIT_CARD_VERIFICATION_CODE) {
2323      field->value = cvc;
2324      break;
2325    }
2326  }
2327}
2328
2329string16 AutofillDialogControllerImpl::GetValueFromSection(
2330    DialogSection section,
2331    AutofillFieldType type) {
2332  DCHECK(SectionIsActive(section));
2333
2334  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2335  if (wrapper)
2336    return wrapper->GetInfo(type);
2337
2338  DetailOutputMap output;
2339  view_->GetUserInput(section, &output);
2340  for (DetailOutputMap::iterator iter = output.begin(); iter != output.end();
2341       ++iter) {
2342    if (iter->first->type == type)
2343      return iter->second;
2344  }
2345
2346  return string16();
2347}
2348
2349void AutofillDialogControllerImpl::SaveProfileGleanedFromSection(
2350    const AutofillProfile& profile,
2351    DialogSection section) {
2352  if (section == SECTION_EMAIL) {
2353    // Save the email address to the existing (suggested) billing profile. If
2354    // there is no existing profile, the newly created one will pick up this
2355    // email, so in that case do nothing.
2356    scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(SECTION_BILLING);
2357    if (wrapper) {
2358      std::string item_key = SuggestionsMenuModelForSection(SECTION_BILLING)->
2359          GetItemKeyForCheckedItem();
2360      AutofillProfile* billing_profile =
2361          GetManager()->GetProfileByGUID(item_key);
2362      billing_profile->OverwriteWithOrAddTo(
2363          profile,
2364          g_browser_process->GetApplicationLocale());
2365    }
2366  } else {
2367    GetManager()->SaveImportedProfile(profile);
2368  }
2369}
2370
2371SuggestionsMenuModel* AutofillDialogControllerImpl::
2372    SuggestionsMenuModelForSection(DialogSection section) {
2373  switch (section) {
2374    case SECTION_EMAIL:
2375      return &suggested_email_;
2376    case SECTION_CC:
2377      return &suggested_cc_;
2378    case SECTION_BILLING:
2379      return &suggested_billing_;
2380    case SECTION_SHIPPING:
2381      return &suggested_shipping_;
2382    case SECTION_CC_BILLING:
2383      return &suggested_cc_billing_;
2384  }
2385
2386  NOTREACHED();
2387  return NULL;
2388}
2389
2390const SuggestionsMenuModel* AutofillDialogControllerImpl::
2391    SuggestionsMenuModelForSection(DialogSection section) const {
2392  return const_cast<AutofillDialogControllerImpl*>(this)->
2393      SuggestionsMenuModelForSection(section);
2394}
2395
2396DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
2397    const SuggestionsMenuModel& model) {
2398  if (&model == &suggested_email_)
2399    return SECTION_EMAIL;
2400
2401  if (&model == &suggested_cc_)
2402    return SECTION_CC;
2403
2404  if (&model == &suggested_billing_)
2405    return SECTION_BILLING;
2406
2407  if (&model == &suggested_cc_billing_)
2408    return SECTION_CC_BILLING;
2409
2410  DCHECK_EQ(&model, &suggested_shipping_);
2411  return SECTION_SHIPPING;
2412}
2413
2414DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
2415    DialogSection section) {
2416  return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
2417}
2418
2419void AutofillDialogControllerImpl::HidePopup() {
2420  if (popup_controller_.get())
2421    popup_controller_->Hide();
2422  input_showing_popup_ = NULL;
2423}
2424
2425bool AutofillDialogControllerImpl::IsEditingExistingData(
2426    DialogSection section) const {
2427  return section_editing_state_.count(section) > 0;
2428}
2429
2430void AutofillDialogControllerImpl::SetEditingExistingData(
2431    DialogSection section, bool editing) {
2432  if (editing)
2433    section_editing_state_.insert(section);
2434  else
2435    section_editing_state_.erase(section);
2436}
2437
2438bool AutofillDialogControllerImpl::IsManuallyEditingSection(
2439    DialogSection section) const {
2440  return IsEditingExistingData(section) ||
2441         SuggestionsMenuModelForSection(section)->
2442             GetItemKeyForCheckedItem() == kAddNewItemKey;
2443}
2444
2445bool AutofillDialogControllerImpl::IsASuggestionItemKey(
2446    const std::string& key) const {
2447  return !key.empty() &&
2448      key != kAddNewItemKey &&
2449      key != kManageItemsKey &&
2450      key != kSameAsBillingKey;
2451}
2452
2453bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
2454  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2455    if (IsManuallyEditingSection(static_cast<DialogSection>(section)))
2456      return true;
2457  }
2458  return false;
2459}
2460
2461bool AutofillDialogControllerImpl::InputIsEditable(
2462    const DetailInput& input,
2463    DialogSection section) const {
2464  if (input.type != CREDIT_CARD_NUMBER || !IsPayingWithWallet())
2465    return true;
2466
2467  if (IsEditingExistingData(section))
2468    return false;
2469
2470  return true;
2471}
2472
2473bool AutofillDialogControllerImpl::AllSectionsAreValid() const {
2474  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
2475    if (!SectionIsValid(static_cast<DialogSection>(section)))
2476      return false;
2477  }
2478  return true;
2479}
2480
2481bool AutofillDialogControllerImpl::SectionIsValid(
2482    DialogSection section) const {
2483  if (!IsManuallyEditingSection(section))
2484    return true;
2485
2486  DetailOutputMap detail_outputs;
2487  view_->GetUserInput(section, &detail_outputs);
2488  return InputsAreValid(detail_outputs, VALIDATE_EDIT).empty();
2489}
2490
2491bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
2492    const base::string16& year,
2493    const base::string16& month) const {
2494  // If the expiration is in the past as per the local clock, it's invalid.
2495  base::Time now = base::Time::Now();
2496  if (!autofill::IsValidCreditCardExpirationDate(year, month, now))
2497    return false;
2498
2499  if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING)) {
2500    const wallet::WalletItems::MaskedInstrument* instrument =
2501        ActiveInstrument();
2502    const std::string& locale = g_browser_process->GetApplicationLocale();
2503    int month_int;
2504    if (base::StringToInt(month, &month_int) &&
2505        instrument->status() ==
2506            wallet::WalletItems::MaskedInstrument::EXPIRED &&
2507        year == instrument->GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, locale) &&
2508        month_int == instrument->expiration_month()) {
2509      // Otherwise, if the user is editing an instrument that's deemed expired
2510      // by the Online Wallet server, mark it invalid on selection.
2511      return false;
2512    }
2513  }
2514
2515  return true;
2516}
2517
2518bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
2519  return SectionIsActive(SECTION_SHIPPING) &&
2520      suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey;
2521}
2522
2523bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
2524  // It's possible that the user checked [X] Save details locally before
2525  // switching payment methods, so only ask the view whether to save details
2526  // locally if that checkbox is showing (currently if not paying with wallet).
2527  // Also, if the user isn't editing any sections, there's no data to save
2528  // locally.
2529  return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
2530}
2531
2532void AutofillDialogControllerImpl::SetIsSubmitting(bool submitting) {
2533  is_submitting_ = submitting;
2534
2535  if (!submitting)
2536    full_wallet_.reset();
2537
2538  if (view_) {
2539    view_->UpdateButtonStrip();
2540    view_->UpdateNotificationArea();
2541  }
2542}
2543
2544bool AutofillDialogControllerImpl::AreLegalDocumentsCurrent() const {
2545  return has_accepted_legal_documents_ ||
2546      (wallet_items_ && wallet_items_->legal_documents().empty());
2547}
2548
2549void AutofillDialogControllerImpl::SubmitWithWallet() {
2550  // TODO(dbeam): disallow interacting with the dialog while submitting.
2551  // http://crbug.com/230932
2552
2553  active_instrument_id_.clear();
2554  active_address_id_.clear();
2555  full_wallet_.reset();
2556
2557  content::BrowserThread::PostTask(
2558     content::BrowserThread::IO, FROM_HERE,
2559     base::Bind(&UserDidOptIntoLocationServices));
2560
2561  GetWalletClient()->AcceptLegalDocuments(
2562      wallet_items_->legal_documents(),
2563      wallet_items_->google_transaction_id(),
2564      source_url_);
2565
2566  if (AreLegalDocumentsCurrent())
2567    LoadRiskFingerprintData();
2568
2569  const wallet::WalletItems::MaskedInstrument* active_instrument =
2570      ActiveInstrument();
2571  if (!IsManuallyEditingSection(SECTION_CC_BILLING)) {
2572    active_instrument_id_ = active_instrument->object_id();
2573    DCHECK(!active_instrument_id_.empty());
2574  }
2575
2576  const wallet::Address* active_address = ActiveShippingAddress();
2577  if (!IsManuallyEditingSection(SECTION_SHIPPING) &&
2578      !ShouldUseBillingForShipping()) {
2579    active_address_id_ = active_address->object_id();
2580    DCHECK(!active_address_id_.empty());
2581  }
2582
2583  scoped_ptr<wallet::Instrument> inputted_instrument =
2584      CreateTransientInstrument();
2585  scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest> update_request =
2586      CreateUpdateInstrumentRequest(
2587          inputted_instrument.get(),
2588          !IsEditingExistingData(SECTION_CC_BILLING) ? std::string() :
2589              active_instrument->object_id());
2590
2591  scoped_ptr<wallet::Address> inputted_address;
2592  if (active_address_id_.empty()) {
2593    if (ShouldUseBillingForShipping()) {
2594      const wallet::Address& address = inputted_instrument ?
2595          inputted_instrument->address() : active_instrument->address();
2596      // Try to find an exact matched shipping address and use it for shipping,
2597      // otherwise save it as a new shipping address. http://crbug.com/225442
2598      const wallet::Address* duplicated_address =
2599          FindDuplicateAddress(wallet_items_->addresses(), address);
2600      if (duplicated_address) {
2601        active_address_id_ = duplicated_address->object_id();
2602        DCHECK(!active_address_id_.empty());
2603      } else {
2604        inputted_address.reset(new wallet::Address(address));
2605        DCHECK(inputted_address->object_id().empty());
2606      }
2607    } else {
2608      inputted_address = CreateTransientAddress();
2609      if (IsEditingExistingData(SECTION_SHIPPING)) {
2610        inputted_address->set_object_id(active_address->object_id());
2611        DCHECK(!inputted_address->object_id().empty());
2612      }
2613    }
2614  }
2615
2616  // If there's neither an address nor instrument to save, |GetFullWallet()|
2617  // is called when the risk fingerprint is loaded.
2618  if (!active_instrument_id_.empty() && !active_address_id_.empty())
2619    return;
2620
2621  // If instrument and address aren't based off of any existing data, save both.
2622  if (inputted_instrument && inputted_address && !update_request &&
2623      inputted_address->object_id().empty()) {
2624    GetWalletClient()->SaveInstrumentAndAddress(
2625        *inputted_instrument,
2626        *inputted_address,
2627        wallet_items_->obfuscated_gaia_id(),
2628        source_url_);
2629    return;
2630  }
2631
2632  if (inputted_instrument) {
2633    if (update_request) {
2634      scoped_ptr<wallet::Address> billing_address(
2635          new wallet::Address(inputted_instrument->address()));
2636      GetWalletClient()->UpdateInstrument(*update_request,
2637                                          billing_address.Pass());
2638    } else {
2639      GetWalletClient()->SaveInstrument(*inputted_instrument,
2640                                        wallet_items_->obfuscated_gaia_id(),
2641                                        source_url_);
2642    }
2643  }
2644
2645  if (inputted_address) {
2646    if (!inputted_address->object_id().empty())
2647      GetWalletClient()->UpdateAddress(*inputted_address, source_url_);
2648    else
2649      GetWalletClient()->SaveAddress(*inputted_address, source_url_);
2650  }
2651}
2652
2653scoped_ptr<wallet::Instrument> AutofillDialogControllerImpl::
2654    CreateTransientInstrument() {
2655  if (!active_instrument_id_.empty())
2656    return scoped_ptr<wallet::Instrument>();
2657
2658  DetailOutputMap output;
2659  view_->GetUserInput(SECTION_CC_BILLING, &output);
2660
2661  CreditCard card;
2662  AutofillProfile profile;
2663  string16 cvc;
2664  GetBillingInfoFromOutputs(output, &card, &cvc, &profile);
2665
2666  return scoped_ptr<wallet::Instrument>(
2667      new wallet::Instrument(card, cvc, profile));
2668}
2669
2670scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest>
2671    AutofillDialogControllerImpl::CreateUpdateInstrumentRequest(
2672        const wallet::Instrument* instrument,
2673        const std::string& instrument_id) {
2674  if (!instrument || instrument_id.empty())
2675    return scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest>();
2676
2677  scoped_ptr<wallet::WalletClient::UpdateInstrumentRequest> update_request(
2678      new wallet::WalletClient::UpdateInstrumentRequest(
2679          instrument_id, source_url_));
2680  update_request->expiration_month = instrument->expiration_month();
2681  update_request->expiration_year = instrument->expiration_year();
2682  update_request->card_verification_number =
2683      UTF16ToUTF8(instrument->card_verification_number());
2684  update_request->obfuscated_gaia_id = wallet_items_->obfuscated_gaia_id();
2685  return update_request.Pass();
2686}
2687
2688scoped_ptr<wallet::Address>AutofillDialogControllerImpl::
2689    CreateTransientAddress() {
2690  // If not using billing for shipping, just scrape the view.
2691  DetailOutputMap output;
2692  view_->GetUserInput(SECTION_SHIPPING, &output);
2693
2694  AutofillProfile profile;
2695  FillFormGroupFromOutputs(output, &profile);
2696
2697  return scoped_ptr<wallet::Address>(new wallet::Address(profile));
2698}
2699
2700void AutofillDialogControllerImpl::GetFullWallet() {
2701  DCHECK(is_submitting_);
2702  DCHECK(IsPayingWithWallet());
2703  DCHECK(wallet_items_);
2704  DCHECK(!active_instrument_id_.empty());
2705  DCHECK(!active_address_id_.empty());
2706
2707  std::vector<wallet::WalletClient::RiskCapability> capabilities;
2708  capabilities.push_back(wallet::WalletClient::VERIFY_CVC);
2709
2710  GetWalletClient()->GetFullWallet(wallet::WalletClient::FullWalletRequest(
2711      active_instrument_id_,
2712      active_address_id_,
2713      source_url_,
2714      wallet_items_->google_transaction_id(),
2715      capabilities));
2716}
2717
2718void AutofillDialogControllerImpl::GetFullWalletIfReady() {
2719  DCHECK(is_submitting_);
2720  DCHECK(IsPayingWithWallet());
2721
2722  if (!active_instrument_id_.empty() && !active_address_id_.empty() &&
2723      !risk_data_.empty()) {
2724    GetFullWallet();
2725  }
2726}
2727
2728void AutofillDialogControllerImpl::HandleSaveOrUpdateRequiredActions(
2729    const std::vector<wallet::RequiredAction>& required_actions) {
2730  DCHECK(!required_actions.empty());
2731
2732  for (std::vector<wallet::RequiredAction>::const_iterator iter =
2733           required_actions.begin();
2734       iter != required_actions.end(); ++iter) {
2735    if (*iter == wallet::INVALID_FORM_FIELD) {
2736      wallet_server_validation_error_ = true;
2737    } else {
2738      // TODO(dbeam): handle this more gracefully.
2739      DisableWallet();
2740    }
2741  }
2742
2743  SetIsSubmitting(false);
2744}
2745
2746void AutofillDialogControllerImpl::FinishSubmit() {
2747  FillOutputForSection(SECTION_EMAIL);
2748  FillOutputForSection(SECTION_CC);
2749  FillOutputForSection(SECTION_BILLING);
2750  FillOutputForSection(SECTION_CC_BILLING);
2751
2752  if (ShouldUseBillingForShipping()) {
2753    FillOutputForSectionWithComparator(
2754        SECTION_BILLING,
2755        base::Bind(DetailInputMatchesShippingField));
2756    FillOutputForSectionWithComparator(
2757        SECTION_CC,
2758        base::Bind(DetailInputMatchesShippingField));
2759    FillOutputForSectionWithComparator(
2760        SECTION_CC_BILLING,
2761        base::Bind(DetailInputMatchesShippingField));
2762  } else {
2763    FillOutputForSection(SECTION_SHIPPING);
2764  }
2765
2766  if (IsPayingWithWallet()) {
2767    profile_->GetPrefs()->SetBoolean(
2768        ::prefs::kAutofillDialogHasPaidWithWallet, true);
2769  } else {
2770    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2771      DialogSection section = static_cast<DialogSection>(i);
2772      if (!SectionIsActive(section))
2773        continue;
2774
2775      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2776      std::string item_key = model->GetItemKeyForCheckedItem();
2777      if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) {
2778        int variant = GetSelectedVariantForModel(*model);
2779        PersistAutofillChoice(section, item_key, variant);
2780      }
2781    }
2782  }
2783
2784  // On a successful submit, if the user manually selected "pay without wallet",
2785  // stop trying to pay with Wallet on future runs of the dialog. On the other
2786  // hand, if there was an error that prevented the user from having the choice
2787  // of using Wallet, leave the pref alone.
2788  if (!account_chooser_model_.had_wallet_error() &&
2789      account_chooser_model_.HasAccountsToChoose()) {
2790    profile_->GetPrefs()->SetBoolean(
2791        ::prefs::kAutofillDialogPayWithoutWallet,
2792        !account_chooser_model_.WalletIsSelected());
2793  }
2794
2795  if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT) {
2796    // Stop observing PersonalDataManager to avoid the dialog redrawing while
2797    // in an Autocheckout flow.
2798    GetManager()->RemoveObserver(this);
2799    autocheckout_started_timestamp_ = base::Time::Now();
2800    DCHECK_EQ(AUTOCHECKOUT_NOT_STARTED, autocheckout_state_);
2801    autocheckout_state_ = AUTOCHECKOUT_IN_PROGRESS;
2802    view_->UpdateButtonStrip();
2803    view_->UpdateDetailArea();
2804    view_->UpdateNotificationArea();
2805  }
2806
2807  LogOnFinishSubmitMetrics();
2808
2809  // Callback should be called as late as possible.
2810  callback_.Run(&form_structure_, !wallet_items_ ? std::string() :
2811      wallet_items_->google_transaction_id());
2812  callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
2813
2814  // This might delete us.
2815  if (GetDialogType() == DIALOG_TYPE_REQUEST_AUTOCOMPLETE)
2816    Hide();
2817}
2818
2819void AutofillDialogControllerImpl::PersistAutofillChoice(
2820    DialogSection section,
2821    const std::string& guid,
2822    int variant) {
2823  DCHECK(!IsPayingWithWallet());
2824  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
2825  value->SetString(kGuidPrefKey, guid);
2826  value->SetInteger(kVariantPrefKey, variant);
2827
2828  DictionaryPrefUpdate updater(profile()->GetPrefs(),
2829                               ::prefs::kAutofillDialogAutofillDefault);
2830  base::DictionaryValue* autofill_choice = updater.Get();
2831  autofill_choice->Set(SectionToPrefString(section), value.release());
2832}
2833
2834void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
2835    DialogSection section,
2836    std::string* guid,
2837    int* variant) {
2838  DCHECK(!IsPayingWithWallet());
2839  // The default choice is the first thing in the menu that is a suggestion
2840  // item.
2841  *variant = 0;
2842  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2843  for (int i = 0; i < model->GetItemCount(); ++i) {
2844    if (IsASuggestionItemKey(model->GetItemKeyAt(i))) {
2845      *guid = model->GetItemKeyAt(i);
2846      break;
2847    }
2848  }
2849}
2850
2851bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section,
2852                                                     std::string* guid,
2853                                                     int* variant) {
2854  DCHECK(!IsPayingWithWallet());
2855  const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary(
2856      ::prefs::kAutofillDialogAutofillDefault);
2857  if (!choices)
2858    return false;
2859
2860  const base::DictionaryValue* choice = NULL;
2861  if (!choices->GetDictionary(SectionToPrefString(section), &choice))
2862    return false;
2863
2864  choice->GetString(kGuidPrefKey, guid);
2865  choice->GetInteger(kVariantPrefKey, variant);
2866  return true;
2867}
2868
2869size_t AutofillDialogControllerImpl::GetSelectedVariantForModel(
2870    const SuggestionsMenuModel& model) {
2871  size_t variant = 0;
2872  // Calculate the variant by looking at how many items come from the same
2873  // data model.
2874  for (int i = model.checked_item() - 1; i >= 0; --i) {
2875    if (model.GetItemKeyAt(i) == model.GetItemKeyForCheckedItem())
2876      variant++;
2877    else
2878      break;
2879  }
2880  return variant;
2881}
2882
2883void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
2884  GetMetricLogger().LogDialogUiDuration(
2885      base::Time::Now() - dialog_shown_timestamp_,
2886      GetDialogType(),
2887      AutofillMetrics::DIALOG_ACCEPTED);
2888
2889  GetMetricLogger().LogDialogUiEvent(
2890      GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED);
2891
2892  AutofillMetrics::DialogDismissalState dismissal_state;
2893  if (!IsManuallyEditingAnySection())
2894    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_DATA;
2895  else if (IsPayingWithWallet())
2896    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_WALLET;
2897  else if (ShouldSaveDetailsLocally())
2898    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL;
2899  else
2900    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE;
2901
2902  GetMetricLogger().LogDialogDismissalState(GetDialogType(), dismissal_state);
2903}
2904
2905void AutofillDialogControllerImpl::LogOnCancelMetrics() {
2906  GetMetricLogger().LogDialogUiEvent(
2907      GetDialogType(), AutofillMetrics::DIALOG_UI_CANCELED);
2908
2909  AutofillMetrics::DialogDismissalState dismissal_state;
2910  if (!IsManuallyEditingAnySection())
2911    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS;
2912  else if (AllSectionsAreValid())
2913    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS;
2914  else
2915    dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS;
2916
2917  GetMetricLogger().LogDialogDismissalState(GetDialogType(), dismissal_state);
2918
2919  GetMetricLogger().LogDialogUiDuration(
2920      base::Time::Now() - dialog_shown_timestamp_,
2921      GetDialogType(),
2922      AutofillMetrics::DIALOG_CANCELED);
2923}
2924
2925void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
2926    const SuggestionsMenuModel& model) {
2927  DialogSection section = SectionForSuggestionsMenuModel(model);
2928
2929  AutofillMetrics::DialogUiEvent dialog_ui_event;
2930  if (model.GetItemKeyForCheckedItem() == kAddNewItemKey) {
2931    // Selected to add a new item.
2932    dialog_ui_event = DialogSectionToUiItemAddedEvent(section);
2933  } else if (IsASuggestionItemKey(model.GetItemKeyForCheckedItem())) {
2934    // Selected an existing item.
2935    dialog_ui_event = DialogSectionToUiSelectionChangedEvent(section);
2936  } else {
2937    // TODO(estade): add logging for "Manage items" or "Use billing for
2938    // shipping"?
2939    return;
2940  }
2941
2942  GetMetricLogger().LogDialogUiEvent(GetDialogType(), dialog_ui_event);
2943}
2944
2945void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
2946  if (was_ui_latency_logged_)
2947    return;
2948
2949  GetMetricLogger().LogDialogLatencyToShow(
2950      GetDialogType(),
2951      base::Time::Now() - dialog_shown_timestamp_);
2952  was_ui_latency_logged_ = true;
2953}
2954
2955AutofillMetrics::DialogInitialUserStateMetric
2956    AutofillDialogControllerImpl::GetInitialUserState() const {
2957  // Consider a user to be an Autofill user if the user has any credit cards
2958  // or addresses saved. Check that the item count is greater than 2 because
2959  // an "empty" menu still has the "add new" menu item and "manage" menu item.
2960  const bool has_autofill_profiles =
2961      suggested_cc_.GetItemCount() > 2 ||
2962      suggested_billing_.GetItemCount() > 2;
2963
2964  if (SignedInState() != SIGNED_IN) {
2965    // Not signed in.
2966    return has_autofill_profiles ?
2967        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL :
2968        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL;
2969  }
2970
2971  // Signed in.
2972  if (wallet_items_->instruments().empty()) {
2973    // No Wallet items.
2974    return has_autofill_profiles ?
2975        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL :
2976        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL;
2977  }
2978
2979  // Has Wallet items.
2980  return has_autofill_profiles ?
2981      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL :
2982      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL;
2983}
2984
2985}  // namespace autofill
2986