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