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