autofill_dialog_controller_impl.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
6
7#include <algorithm>
8#include <map>
9#include <string>
10
11#include "apps/shell_window.h"
12#include "apps/shell_window_registry.h"
13#include "apps/ui/native_app_window.h"
14#include "base/base64.h"
15#include "base/bind.h"
16#include "base/i18n/case_conversion.h"
17#include "base/i18n/rtl.h"
18#include "base/logging.h"
19#include "base/prefs/pref_registry_simple.h"
20#include "base/prefs/pref_service.h"
21#include "base/prefs/scoped_user_pref_update.h"
22#include "base/rand_util.h"
23#include "base/strings/string_number_conversions.h"
24#include "base/strings/string_split.h"
25#include "base/strings/string_util.h"
26#include "base/strings/utf_string_conversions.h"
27#include "base/time/time.h"
28#include "chrome/browser/autofill/personal_data_manager_factory.h"
29#include "chrome/browser/browser_process.h"
30#include "chrome/browser/profiles/profile.h"
31#include "chrome/browser/ui/autofill/autofill_dialog_common.h"
32#include "chrome/browser/ui/autofill/autofill_dialog_view.h"
33#include "chrome/browser/ui/autofill/data_model_wrapper.h"
34#if !defined(OS_ANDROID)
35#include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h"
36#include "chrome/browser/ui/autofill/new_credit_card_bubble_controller.h"
37#endif
38#include "chrome/browser/ui/browser.h"
39#include "chrome/browser/ui/browser_finder.h"
40#include "chrome/browser/ui/browser_navigator.h"
41#include "chrome/browser/ui/browser_window.h"
42#include "chrome/common/chrome_version_info.h"
43#include "chrome/common/pref_names.h"
44#include "chrome/common/render_messages.h"
45#include "chrome/common/url_constants.h"
46#include "components/autofill/content/browser/risk/fingerprint.h"
47#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
48#include "components/autofill/content/browser/wallet/form_field_error.h"
49#include "components/autofill/content/browser/wallet/full_wallet.h"
50#include "components/autofill/content/browser/wallet/instrument.h"
51#include "components/autofill/content/browser/wallet/wallet_address.h"
52#include "components/autofill/content/browser/wallet/wallet_items.h"
53#include "components/autofill/content/browser/wallet/wallet_service_url.h"
54#include "components/autofill/content/browser/wallet/wallet_signin_helper.h"
55#include "components/autofill/core/browser/autofill_country.h"
56#include "components/autofill/core/browser/autofill_data_model.h"
57#include "components/autofill/core/browser/autofill_manager.h"
58#include "components/autofill/core/browser/autofill_type.h"
59#include "components/autofill/core/browser/personal_data_manager.h"
60#include "components/autofill/core/browser/phone_number_i18n.h"
61#include "components/autofill/core/browser/validation.h"
62#include "components/autofill/core/common/form_data.h"
63#include "components/user_prefs/pref_registry_syncable.h"
64#include "content/public/browser/browser_thread.h"
65#include "content/public/browser/geolocation_provider.h"
66#include "content/public/browser/navigation_controller.h"
67#include "content/public/browser/navigation_details.h"
68#include "content/public/browser/navigation_entry.h"
69#include "content/public/browser/notification_service.h"
70#include "content/public/browser/notification_types.h"
71#include "content/public/browser/render_view_host.h"
72#include "content/public/browser/web_contents.h"
73#include "content/public/browser/web_contents_view.h"
74#include "content/public/common/url_constants.h"
75#include "grit/chromium_strings.h"
76#include "grit/component_strings.h"
77#include "grit/generated_resources.h"
78#include "grit/platform_locale_settings.h"
79#include "grit/theme_resources.h"
80#include "grit/webkit_resources.h"
81#include "net/cert/cert_status_flags.h"
82#include "ui/base/base_window.h"
83#include "ui/base/l10n/l10n_util.h"
84#include "ui/base/models/combobox_model.h"
85#include "ui/base/resource/resource_bundle.h"
86#include "ui/events/event.h"
87#include "ui/gfx/canvas.h"
88#include "ui/gfx/image/image_skia_operations.h"
89#include "ui/gfx/skia_util.h"
90
91namespace autofill {
92
93namespace {
94
95const char kAddNewItemKey[] = "add-new-item";
96const char kManageItemsKey[] = "manage-items";
97const char kSameAsBillingKey[] = "same-as-billing";
98
99// URLs for Wallet error messages.
100const char kBuyerLegalAddressStatusUrl[] =
101    "https://wallet.google.com/manage/settings";
102const char kKnowYourCustomerStatusUrl[] = "https://wallet.google.com/kyc";
103
104// Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
105// these values).
106const char kGuidPrefKey[] = "guid";
107
108// This string is stored along with saved addresses and credit cards in the
109// WebDB, and hence should not be modified, so that it remains consistent over
110// time.
111const char kAutofillDialogOrigin[] = "Chrome Autofill dialog";
112
113// HSL shift to gray out an image.
114const color_utils::HSL kGrayImageShift = {-1, 0, 0.8};
115
116// Limit Wallet items refresh rate to at most once per minute.
117const int64 kWalletItemsRefreshRateSeconds = 60;
118
119// The number of milliseconds to delay enabling the submit button after showing
120// the dialog. This delay prevents users from accidentally clicking the submit
121// button on startup.
122const int kSubmitButtonDelayMs = 1000;
123
124// A helper class to make sure an AutofillDialogView knows when a series of
125// updates is incoming.
126class ScopedViewUpdates {
127 public:
128  explicit ScopedViewUpdates(AutofillDialogView* view) : view_(view) {
129    if (view_)
130      view_->UpdatesStarted();
131  }
132
133  ~ScopedViewUpdates() {
134    if (view_)
135      view_->UpdatesFinished();
136  }
137
138 private:
139  AutofillDialogView* view_;
140
141  DISALLOW_COPY_AND_ASSIGN(ScopedViewUpdates);
142};
143
144// Returns true if |input| should be used to fill a site-requested |field| which
145// is notated with a "shipping" tag, for use when the user has decided to use
146// the billing address as the shipping address.
147bool DetailInputMatchesShippingField(const DetailInput& input,
148                                     const AutofillField& field) {
149  // Equivalent billing field type is used to support UseBillingAsShipping
150  // usecase.
151  ServerFieldType field_type =
152      AutofillType::GetEquivalentBillingFieldType(
153          field.Type().GetStorableType());
154
155  return common::InputTypeMatchesFieldType(input, AutofillType(field_type));
156}
157
158// Initializes |form_group| from user-entered data.
159void FillFormGroupFromOutputs(const FieldValueMap& detail_outputs,
160                              FormGroup* form_group) {
161  for (FieldValueMap::const_iterator iter = detail_outputs.begin();
162       iter != detail_outputs.end(); ++iter) {
163    ServerFieldType type = iter->first;
164    if (!iter->second.empty()) {
165      if (type == ADDRESS_HOME_COUNTRY || type == ADDRESS_BILLING_COUNTRY) {
166        form_group->SetInfo(AutofillType(type),
167                            iter->second,
168                            g_browser_process->GetApplicationLocale());
169      } else {
170        form_group->SetRawInfo(
171            AutofillType(type).GetStorableType(), iter->second);
172      }
173    }
174  }
175}
176
177// Get billing info from |output| and put it into |card|, |cvc|, and |profile|.
178// These outparams are required because |card|/|profile| accept different types
179// of raw info, and CreditCard doesn't save CVCs.
180void GetBillingInfoFromOutputs(const FieldValueMap& output,
181                               CreditCard* card,
182                               base::string16* cvc,
183                               AutofillProfile* profile) {
184  for (FieldValueMap::const_iterator it = output.begin();
185       it != output.end(); ++it) {
186    const ServerFieldType type = it->first;
187    base::string16 trimmed;
188    TrimWhitespace(it->second, TRIM_ALL, &trimmed);
189
190    // Special case CVC as CreditCard just swallows it.
191    if (type == CREDIT_CARD_VERIFICATION_CODE) {
192      if (cvc)
193        cvc->assign(trimmed);
194    } else if (type == ADDRESS_HOME_COUNTRY ||
195               type == ADDRESS_BILLING_COUNTRY) {
196      if (profile) {
197        profile->SetInfo(AutofillType(type),
198                         trimmed,
199                         g_browser_process->GetApplicationLocale());
200      }
201    } else {
202      // Copy the credit card name to |profile| in addition to |card| as
203      // wallet::Instrument requires a recipient name for its billing address.
204      if (card && type == NAME_FULL)
205        card->SetRawInfo(CREDIT_CARD_NAME, trimmed);
206
207      if (common::IsCreditCardType(type)) {
208        if (card)
209          card->SetRawInfo(type, trimmed);
210      } else if (profile) {
211        profile->SetRawInfo(AutofillType(type).GetStorableType(), trimmed);
212      }
213    }
214  }
215}
216
217// Returns the containing window for the given |web_contents|. The containing
218// window might be a browser window for a Chrome tab, or it might be a shell
219// window for a platform app.
220ui::BaseWindow* GetBaseWindowForWebContents(
221    const content::WebContents* web_contents) {
222  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
223  if (browser)
224    return browser->window();
225
226  gfx::NativeWindow native_window =
227      web_contents->GetView()->GetTopLevelNativeWindow();
228  apps::ShellWindow* shell_window =
229      apps::ShellWindowRegistry::
230          GetShellWindowForNativeWindowAnyProfile(native_window);
231  return shell_window->GetBaseWindow();
232}
233
234// Returns a string descriptor for a DialogSection, for use with prefs (do not
235// change these values).
236std::string SectionToPrefString(DialogSection section) {
237  switch (section) {
238    case SECTION_CC:
239      return "cc";
240
241    case SECTION_BILLING:
242      return "billing";
243
244    case SECTION_CC_BILLING:
245      // The SECTION_CC_BILLING section isn't active when using Autofill.
246      NOTREACHED();
247      return std::string();
248
249    case SECTION_SHIPPING:
250      return "shipping";
251  }
252
253  NOTREACHED();
254  return std::string();
255}
256
257// Check if a given MaskedInstrument is allowed for the purchase.
258bool IsInstrumentAllowed(
259    const wallet::WalletItems::MaskedInstrument& instrument) {
260  switch (instrument.status()) {
261    case wallet::WalletItems::MaskedInstrument::VALID:
262    case wallet::WalletItems::MaskedInstrument::PENDING:
263    case wallet::WalletItems::MaskedInstrument::EXPIRED:
264    case wallet::WalletItems::MaskedInstrument::BILLING_INCOMPLETE:
265      return true;
266    default:
267      return false;
268  }
269}
270
271// Signals that the user has opted in to geolocation services.  Factored out
272// into a separate method because all interaction with the geolocation provider
273// needs to happen on the IO thread, which is not the thread
274// AutofillDialogViewDelegate lives on.
275void UserDidOptIntoLocationServices() {
276  content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
277}
278
279// Returns whether |data_model| is complete, i.e. can fill out all the
280// |requested_fields|, and verified, i.e. not just automatically aggregated.
281// Incomplete or unverifed data will not be displayed in the dropdown menu.
282bool HasCompleteAndVerifiedData(const AutofillDataModel& data_model,
283                                const DetailInputs& requested_fields) {
284  if (!data_model.IsVerified())
285    return false;
286
287  for (size_t i = 0; i < requested_fields.size(); ++i) {
288    ServerFieldType type =
289        AutofillType(requested_fields[i].type).GetStorableType();
290    if (type != ADDRESS_HOME_LINE2 &&
291        type != CREDIT_CARD_VERIFICATION_CODE &&
292        data_model.GetRawInfo(type).empty()) {
293      return false;
294    }
295  }
296
297  return true;
298}
299
300// Returns true if |profile| has an invalid address, i.e. an invalid state, zip
301// code, phone number, or email address. Otherwise returns false. Profiles with
302// invalid addresses are not suggested in the dropdown menu for billing and
303// shipping addresses.
304bool HasInvalidAddress(const AutofillProfile& profile) {
305  return profile.IsPresentButInvalid(ADDRESS_HOME_STATE) ||
306         profile.IsPresentButInvalid(ADDRESS_HOME_ZIP) ||
307         profile.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER);
308}
309
310// Loops through |addresses_| comparing to |address| ignoring ID. If a match
311// is not found, NULL is returned.
312const wallet::Address* FindDuplicateAddress(
313    const std::vector<wallet::Address*>& addresses,
314    const wallet::Address& address) {
315  for (size_t i = 0; i < addresses.size(); ++i) {
316    if (addresses[i]->EqualsIgnoreID(address))
317      return addresses[i];
318  }
319  return NULL;
320}
321
322bool IsCardHolderNameValidForWallet(const string16& name) {
323  base::string16 whitespace_collapsed_name = CollapseWhitespace(name, true);
324  std::vector<base::string16> split_name;
325  base::SplitString(whitespace_collapsed_name, ' ', &split_name);
326  return split_name.size() >= 2;
327}
328
329DialogSection SectionFromLocation(wallet::FormFieldError::Location location) {
330  switch (location) {
331    case wallet::FormFieldError::PAYMENT_INSTRUMENT:
332    case wallet::FormFieldError::LEGAL_ADDRESS:
333      return SECTION_CC_BILLING;
334
335    case wallet::FormFieldError::SHIPPING_ADDRESS:
336      return SECTION_SHIPPING;
337
338    case wallet::FormFieldError::UNKNOWN_LOCATION:
339      NOTREACHED();
340      return SECTION_MAX;
341  }
342
343  NOTREACHED();
344  return SECTION_MAX;
345}
346
347scoped_ptr<DialogNotification> GetWalletError(
348    wallet::WalletClient::ErrorType error_type) {
349  base::string16 text;
350  GURL url;
351
352  switch (error_type) {
353    case wallet::WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS:
354      text = l10n_util::GetStringUTF16(
355          IDS_AUTOFILL_WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS);
356      url = GURL(kKnowYourCustomerStatusUrl);
357      break;
358
359    case wallet::WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED:
360      text = l10n_util::GetStringUTF16(
361          IDS_AUTOFILL_WALLET_BUYER_COUNTRY_NOT_SUPPORTED);
362      url = GURL(kBuyerLegalAddressStatusUrl);
363      break;
364
365    default:
366      // The notification will not have a link; it's handled in the next
367      // switch statement.
368      break;
369  }
370
371  if (!text.empty()) {
372    scoped_ptr<DialogNotification> notification(new DialogNotification(
373        DialogNotification::WALLET_ERROR,
374        text));
375    notification->set_link_url(url);
376    return notification.Pass();
377  }
378
379  int error_ids = 0;
380  int error_code = 0;
381
382  switch (error_type) {
383    case wallet::WalletClient::UNSUPPORTED_MERCHANT:
384      error_ids = IDS_AUTOFILL_WALLET_UNSUPPORTED_MERCHANT;
385      break;
386
387    case wallet::WalletClient::BAD_REQUEST:
388      error_ids = IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR;
389      error_code = 71;
390      break;
391
392    case wallet::WalletClient::INVALID_PARAMS:
393      error_ids = IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR;
394      error_code = 42;
395      break;
396
397    case wallet::WalletClient::BUYER_ACCOUNT_ERROR:
398      error_ids = IDS_AUTOFILL_WALLET_BUYER_ACCOUNT_ERROR;
399      error_code = 12;
400      break;
401
402    case wallet::WalletClient::UNSUPPORTED_API_VERSION:
403      error_ids = IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR;
404      error_code = 43;
405      break;
406
407    case wallet::WalletClient::SERVICE_UNAVAILABLE:
408      error_ids = IDS_AUTOFILL_WALLET_SERVICE_UNAVAILABLE_ERROR;
409      error_code = 61;
410      break;
411
412    case wallet::WalletClient::INTERNAL_ERROR:
413      error_ids = IDS_AUTOFILL_WALLET_UNKNOWN_ERROR;
414      error_code = 62;
415      break;
416
417    case wallet::WalletClient::MALFORMED_RESPONSE:
418      error_ids = IDS_AUTOFILL_WALLET_UNKNOWN_ERROR;
419      error_code = 72;
420      break;
421
422    case wallet::WalletClient::NETWORK_ERROR:
423      error_ids = IDS_AUTOFILL_WALLET_UNKNOWN_ERROR;
424      error_code = 73;
425      break;
426
427    case wallet::WalletClient::UNKNOWN_ERROR:
428      error_ids = IDS_AUTOFILL_WALLET_UNKNOWN_ERROR;
429      error_code = 74;
430      break;
431
432    default:
433      break;
434  }
435
436  DCHECK_NE(0, error_ids);
437
438  // The other error types are strings of the form "XXX. You can pay without
439  // wallet."
440  scoped_ptr<DialogNotification> notification(new DialogNotification(
441      DialogNotification::WALLET_ERROR,
442      l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_COMPLETE_WITHOUT_WALLET,
443                                 l10n_util::GetStringUTF16(error_ids))));
444
445  if (error_code) {
446    notification->set_tooltip_text(
447        l10n_util::GetStringFUTF16(IDS_AUTOFILL_WALLET_ERROR_CODE_TOOLTIP,
448                                   base::IntToString16(error_code)));
449  }
450
451  return notification.Pass();
452}
453
454// Returns the ID of the address or instrument that should be selected in the
455// UI, given that the |default_id| is currently the default ID on the Wallet
456// server, |previous_default_id| was the default ID prior to re-fetching the
457// Wallet data, and |previously_selected_id| was the ID of the item selected in
458// the dialog prior to re-fetching the Wallet data.
459std::string GetIdToSelect(const std::string& default_id,
460                          const std::string& previous_default_id,
461                          const std::string& previously_selected_id) {
462  // If the default ID changed since the last fetch of the Wallet data, select
463  // it rather than the previously selected item, as the user's intention in
464  // changing the default was probably to use it.
465  if (default_id != previous_default_id)
466    return default_id;
467
468  // Otherwise, prefer the previously selected item, if there was one.
469  return !previously_selected_id.empty() ? previously_selected_id : default_id;
470}
471
472// Generate a random card number in a user displayable format.
473base::string16 GenerateRandomCardNumber() {
474  std::string card_number;
475  for (size_t i = 0; i < 4; ++i) {
476    int part = base::RandInt(0, 10000);
477    base::StringAppendF(&card_number, "%04d ", part);
478  }
479  return ASCIIToUTF16(card_number);
480}
481
482gfx::Image CreditCardIconForType(const std::string& credit_card_type) {
483  const int input_card_idr = CreditCard::IconResourceId(credit_card_type);
484  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
485  gfx::Image result = rb.GetImageNamed(input_card_idr);
486  if (input_card_idr == IDR_AUTOFILL_CC_GENERIC) {
487    // When the credit card type is unknown, no image should be shown. However,
488    // to simplify the view code on Mac, save space for the credit card image by
489    // returning a transparent image of the appropriate size.
490    result = gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage(
491        result.AsImageSkia(), 0));
492  }
493  return result;
494}
495
496gfx::Image CvcIconForCreditCardType(const base::string16& credit_card_type) {
497  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
498  if (credit_card_type == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX))
499    return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT_AMEX);
500
501  return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT);
502}
503
504}  // namespace
505
506AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {}
507
508AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
509  if (popup_controller_)
510    popup_controller_->Hide();
511
512  GetMetricLogger().LogDialogInitialUserState(initial_user_state_);
513}
514
515// static
516base::WeakPtr<AutofillDialogControllerImpl>
517    AutofillDialogControllerImpl::Create(
518    content::WebContents* contents,
519    const FormData& form_structure,
520    const GURL& source_url,
521    const base::Callback<void(const FormStructure*)>& callback) {
522  // AutofillDialogControllerImpl owns itself.
523  AutofillDialogControllerImpl* autofill_dialog_controller =
524      new AutofillDialogControllerImpl(contents,
525                                       form_structure,
526                                       source_url,
527                                       callback);
528  return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
529}
530
531// static
532void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {
533  registry->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance);
534}
535
536// static
537void AutofillDialogController::RegisterProfilePrefs(
538    user_prefs::PrefRegistrySyncable* registry) {
539  registry->RegisterBooleanPref(
540      ::prefs::kAutofillDialogPayWithoutWallet,
541      false,
542      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
543  registry->RegisterDictionaryPref(
544      ::prefs::kAutofillDialogAutofillDefault,
545      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
546  registry->RegisterBooleanPref(
547      ::prefs::kAutofillDialogSaveData,
548      true,
549      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
550}
551
552// static
553base::WeakPtr<AutofillDialogController> AutofillDialogController::Create(
554    content::WebContents* contents,
555    const FormData& form_structure,
556    const GURL& source_url,
557    const base::Callback<void(const FormStructure*)>& callback) {
558  return AutofillDialogControllerImpl::Create(contents,
559                                              form_structure,
560                                              source_url,
561                                              callback);
562}
563
564void AutofillDialogControllerImpl::Show() {
565  dialog_shown_timestamp_ = base::Time::Now();
566
567  // The Autofill dialog is shown in response to a message from the renderer and
568  // as such, it can only be made in the context of the current document. A call
569  // to GetActiveEntry would return a pending entry, if there was one, which
570  // would be a security bug. Therefore, we use the last committed URL for the
571  // access checks.
572  const GURL& current_url = web_contents()->GetLastCommittedURL();
573  invoked_from_same_origin_ =
574      current_url.GetOrigin() == source_url_.GetOrigin();
575
576  // Log any relevant UI metrics and security exceptions.
577  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
578
579  GetMetricLogger().LogDialogSecurityMetric(
580      AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
581
582  // Determine what field types should be included in the dialog.
583  // Note that RequestingCreditCardInfo() below relies on parsed field types.
584  bool has_types = false;
585  bool has_sections = false;
586  form_structure_.ParseFieldTypesFromAutocompleteAttributes(
587      &has_types, &has_sections);
588
589  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
590    GetMetricLogger().LogDialogSecurityMetric(
591        AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
592  }
593
594  if (!invoked_from_same_origin_) {
595    GetMetricLogger().LogDialogSecurityMetric(
596        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
597  }
598
599  // Fail if the author didn't specify autocomplete types.
600  if (!has_types) {
601    callback_.Run(NULL);
602    delete this;
603    return;
604  }
605
606  common::BuildInputsForSection(SECTION_CC,
607                                &requested_cc_fields_);
608  common::BuildInputsForSection(SECTION_BILLING,
609                                &requested_billing_fields_);
610  common::BuildInputsForSection(SECTION_CC_BILLING,
611                                &requested_cc_billing_fields_);
612  common::BuildInputsForSection(SECTION_SHIPPING,
613                                &requested_shipping_fields_);
614
615  // Test whether we need to show the shipping section. If filling that section
616  // would be a no-op, don't show it.
617  const DetailInputs& inputs = RequestedFieldsForSection(SECTION_SHIPPING);
618  EmptyDataModelWrapper empty_wrapper;
619  cares_about_shipping_ = empty_wrapper.FillFormStructure(
620      inputs,
621      base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING),
622      &form_structure_);
623
624  SuggestionsUpdated();
625  SubmitButtonDelayBegin();
626
627  if (account_chooser_model_.WalletIsSelected())
628    FetchWalletCookie();
629
630  // TODO(estade): don't show the dialog if the site didn't specify the right
631  // fields. First we must figure out what the "right" fields are.
632  view_.reset(CreateView());
633  view_->Show();
634  GetManager()->AddObserver(this);
635
636  if (!account_chooser_model_.WalletIsSelected())
637    LogDialogLatencyToShow();
638}
639
640void AutofillDialogControllerImpl::Hide() {
641  if (view_)
642    view_->Hide();
643}
644
645void AutofillDialogControllerImpl::TabActivated() {
646  // If the user switched away from this tab and then switched back, reload the
647  // Wallet items, in case they've changed.
648  int64 seconds_elapsed_since_last_refresh =
649      (base::TimeTicks::Now() - last_wallet_items_fetch_timestamp_).InSeconds();
650  if (IsPayingWithWallet() && wallet_items_ &&
651      seconds_elapsed_since_last_refresh >= kWalletItemsRefreshRateSeconds) {
652    GetWalletItems();
653  }
654}
655
656TestableAutofillDialogView* AutofillDialogControllerImpl::GetTestableView() {
657  return view_ ? view_->GetTestableView() : NULL;
658}
659
660////////////////////////////////////////////////////////////////////////////////
661// AutofillDialogViewDelegate implementation.
662
663string16 AutofillDialogControllerImpl::DialogTitle() const {
664  if (ShouldShowSpinner())
665    return string16();
666
667  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE);
668}
669
670string16 AutofillDialogControllerImpl::AccountChooserText() const {
671  if (!account_chooser_model_.WalletIsSelected())
672    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PAYING_WITHOUT_WALLET);
673
674  if (SignedInState() == SIGNED_IN)
675    return account_chooser_model_.GetActiveWalletAccountName();
676
677  // In this case, the account chooser should be showing the signin link.
678  return string16();
679}
680
681string16 AutofillDialogControllerImpl::SignInLinkText() const {
682  int ids = SignedInState() == NOT_CHECKED ?
683      IDS_AUTOFILL_DIALOG_USE_WALLET_LINK :
684      ShouldShowSignInWebView() ? IDS_AUTOFILL_DIALOG_CANCEL_SIGN_IN :
685                                  IDS_AUTOFILL_DIALOG_SIGN_IN;
686
687  return l10n_util::GetStringUTF16(ids);
688}
689
690string16 AutofillDialogControllerImpl::SpinnerText() const {
691  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_LOADING);
692}
693
694string16 AutofillDialogControllerImpl::EditSuggestionText() const {
695  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT);
696}
697
698string16 AutofillDialogControllerImpl::CancelButtonText() const {
699  return l10n_util::GetStringUTF16(IDS_CANCEL);
700}
701
702string16 AutofillDialogControllerImpl::ConfirmButtonText() const {
703  return l10n_util::GetStringUTF16(IsSubmitPausedOn(wallet::VERIFY_CVV) ?
704      IDS_AUTOFILL_DIALOG_VERIFY_BUTTON : IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON);
705}
706
707string16 AutofillDialogControllerImpl::SaveLocallyText() const {
708  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX);
709}
710
711string16 AutofillDialogControllerImpl::SaveLocallyTooltip() const {
712  return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_TOOLTIP);
713}
714
715string16 AutofillDialogControllerImpl::LegalDocumentsText() {
716  if (!IsPayingWithWallet() || ShouldShowSignInWebView())
717    return string16();
718
719  return legal_documents_text_;
720}
721
722bool AutofillDialogControllerImpl::ShouldShowSpinner() const {
723  return SignedInState() == REQUIRES_RESPONSE ||
724         SignedInState() == REQUIRES_PASSIVE_SIGN_IN;
725}
726
727bool AutofillDialogControllerImpl::ShouldShowSignInWebView() const {
728  return !signin_registrar_.IsEmpty();
729}
730
731GURL AutofillDialogControllerImpl::SignInUrl() const {
732  return wallet::GetSignInUrl();
733}
734
735bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
736  return !IsPayingWithWallet() &&
737      !profile_->IsOffTheRecord() &&
738      IsManuallyEditingAnySection() &&
739      !ShouldShowSpinner();
740}
741
742bool AutofillDialogControllerImpl::ShouldSaveInChrome() const {
743  return profile_->GetPrefs()->GetBoolean(::prefs::kAutofillDialogSaveData);
744}
745
746int AutofillDialogControllerImpl::GetDialogButtons() const {
747  if (waiting_for_explicit_sign_in_response_)
748    return ui::DIALOG_BUTTON_NONE;
749
750  if (ShouldShowSpinner() && !handling_use_wallet_link_click_)
751    return ui::DIALOG_BUTTON_CANCEL;
752
753  return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
754}
755
756bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
757    ui::DialogButton button) const {
758  if (button == ui::DIALOG_BUTTON_OK) {
759    if (IsSubmitPausedOn(wallet::VERIFY_CVV))
760      return true;
761
762    if (ShouldShowSpinner() || is_submitting_)
763      return false;
764
765    if (submit_button_delay_timer_.IsRunning())
766      return false;
767
768    return true;
769  }
770
771  DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button);
772  return !is_submitting_ || IsSubmitPausedOn(wallet::VERIFY_CVV);
773}
774
775DialogOverlayState AutofillDialogControllerImpl::GetDialogOverlay() {
776  bool show_wallet_interstitial = IsPayingWithWallet() && is_submitting_ &&
777      !(full_wallet_ && !full_wallet_->required_actions().empty());
778  if (!show_wallet_interstitial) {
779    card_scrambling_delay_.Stop();
780    card_scrambling_refresher_.Stop();
781    return DialogOverlayState();
782  }
783
784  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
785  DialogOverlayState state;
786  state.string.font = rb.GetFont(ui::ResourceBundle::BaseFont).DeriveFont(3);
787  state.string.text_color = SK_ColorDKGRAY;
788
789  const SkColor start_top_color = SkColorSetRGB(0xD6, 0xD6, 0xD6);
790  const SkColor start_bottom_color = SkColorSetRGB(0x98, 0x98, 0x98);
791  const SkColor final_top_color = SkColorSetRGB(0x52, 0x9F, 0xF8);
792  const SkColor final_bottom_color = SkColorSetRGB(0x22, 0x75, 0xE5);
793
794  if (full_wallet_ && full_wallet_->required_actions().empty()) {
795    card_scrambling_delay_.Stop();
796    card_scrambling_refresher_.Stop();
797
798    string16 cc_number =
799        full_wallet_->GetInfo(AutofillType(CREDIT_CARD_NUMBER));
800    DCHECK_GE(cc_number.size(), 4U);
801    state.image = GetGeneratedCardImage(
802        ASCIIToUTF16("XXXX XXXX XXXX ") +
803            cc_number.substr(cc_number.size() - 4),
804        full_wallet_->billing_address()->recipient_name(),
805        color_utils::AlphaBlend(
806            final_top_color,
807            start_top_color,
808            255 * card_generated_animation_.GetCurrentValue()),
809        color_utils::AlphaBlend(
810            final_bottom_color,
811            start_bottom_color,
812            255 * card_generated_animation_.GetCurrentValue()));
813
814    state.string.text = l10n_util::GetStringUTF16(
815        IDS_AUTOFILL_DIALOG_CARD_GENERATION_DONE);
816  } else {
817    // Start the refresher if it isn't running. Wait one second before pumping
818    // updates to the view.
819    if (!card_scrambling_delay_.IsRunning() &&
820        !card_scrambling_refresher_.IsRunning()) {
821      scrambled_card_number_ = GenerateRandomCardNumber();
822      card_scrambling_delay_.Start(
823          FROM_HERE,
824          base::TimeDelta::FromSeconds(1),
825          this,
826          &AutofillDialogControllerImpl::StartCardScramblingRefresher);
827    }
828
829    DCHECK(!scrambled_card_number_.empty());
830    state.image = GetGeneratedCardImage(
831        scrambled_card_number_,
832        submitted_cardholder_name_,
833        start_top_color,
834        start_bottom_color);
835
836    // "Submitting" waiting page.
837    state.string.text = l10n_util::GetStringUTF16(
838        IDS_AUTOFILL_DIALOG_CARD_GENERATION_IN_PROGRESS);
839  }
840
841  return state;
842}
843
844const std::vector<gfx::Range>& AutofillDialogControllerImpl::
845    LegalDocumentLinks() {
846  return legal_document_link_ranges_;
847}
848
849bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section)
850    const {
851  if (IsSubmitPausedOn(wallet::VERIFY_CVV))
852    return section == SECTION_CC_BILLING;
853
854  if (!FormStructureCaresAboutSection(section))
855    return false;
856
857  if (IsPayingWithWallet())
858    return section == SECTION_CC_BILLING || section == SECTION_SHIPPING;
859
860  return section != SECTION_CC_BILLING;
861}
862
863void AutofillDialogControllerImpl::GetWalletItems() {
864  ScopedViewUpdates updates(view_.get());
865
866  wallet_items_requested_ = true;
867  wallet::WalletClient* wallet_client = GetWalletClient();
868  wallet_client->CancelRequests();
869
870  previously_selected_instrument_id_.clear();
871  previously_selected_shipping_address_id_.clear();
872  if (wallet_items_) {
873    previous_default_instrument_id_ = wallet_items_->default_instrument_id();
874    previous_default_shipping_address_id_ = wallet_items_->default_address_id();
875
876    const wallet::WalletItems::MaskedInstrument* instrument =
877        ActiveInstrument();
878    if (instrument)
879      previously_selected_instrument_id_ = instrument->object_id();
880
881    const wallet::Address* address = ActiveShippingAddress();
882    if (address)
883      previously_selected_shipping_address_id_ = address->object_id();
884  }
885
886  last_wallet_items_fetch_timestamp_ = base::TimeTicks::Now();
887  passive_failed_ = false;
888  wallet_items_.reset();
889
890  // The "Loading..." page should be showing now, which should cause the
891  // account chooser to hide.
892  view_->UpdateAccountChooser();
893  wallet_client->GetWalletItems();
894}
895
896void AutofillDialogControllerImpl::HideSignIn() {
897  ScopedViewUpdates updates(view_.get());
898  signin_registrar_.RemoveAll();
899  view_->HideSignIn();
900  view_->UpdateAccountChooser();
901}
902
903AutofillDialogControllerImpl::DialogSignedInState
904    AutofillDialogControllerImpl::SignedInState() const {
905  if (wallet_error_notification_)
906    return SIGN_IN_DISABLED;
907
908  if (signin_helper_ || (wallet_items_requested_ && !wallet_items_))
909    return REQUIRES_RESPONSE;
910
911  if (!wallet_items_requested_)
912    return NOT_CHECKED;
913
914  if (wallet_items_->HasRequiredAction(wallet::GAIA_AUTH) ||
915      passive_failed_) {
916    return REQUIRES_SIGN_IN;
917  }
918
919  if (wallet_items_->HasRequiredAction(wallet::PASSIVE_GAIA_AUTH))
920    return REQUIRES_PASSIVE_SIGN_IN;
921
922  return SIGNED_IN;
923}
924
925void AutofillDialogControllerImpl::SignedInStateUpdated() {
926  if (!ShouldShowSpinner())
927    waiting_for_explicit_sign_in_response_ = false;
928
929  switch (SignedInState()) {
930    case SIGNED_IN:
931      LogDialogLatencyToShow();
932      break;
933
934    case REQUIRES_SIGN_IN:
935      if (handling_use_wallet_link_click_)
936        SignInLinkClicked();
937      // Fall through.
938    case SIGN_IN_DISABLED:
939      // Switch to the local account and refresh the dialog.
940      signin_helper_.reset();
941      OnWalletSigninError();
942      handling_use_wallet_link_click_ = false;
943      break;
944
945    case REQUIRES_PASSIVE_SIGN_IN:
946      // Attempt to passively sign in the user.
947      DCHECK(!signin_helper_);
948      signin_helper_.reset(new wallet::WalletSigninHelper(
949          this,
950          profile_->GetRequestContext()));
951      signin_helper_->StartPassiveSignin(GetWalletClient()->user_index());
952      break;
953
954    case NOT_CHECKED:
955    case REQUIRES_RESPONSE:
956      break;
957  }
958}
959
960void AutofillDialogControllerImpl::OnWalletOrSigninUpdate() {
961  ScopedViewUpdates updates(view_.get());
962  SignedInStateUpdated();
963  SuggestionsUpdated();
964  UpdateAccountChooserView();
965
966  if (view_) {
967    view_->UpdateButtonStrip();
968    view_->UpdateOverlay();
969  }
970
971  // On the first successful response, compute the initial user state metric.
972  if (initial_user_state_ == AutofillMetrics::DIALOG_USER_STATE_UNKNOWN)
973    initial_user_state_ = GetInitialUserState();
974}
975
976void AutofillDialogControllerImpl::OnWalletFormFieldError(
977    const std::vector<wallet::FormFieldError>& form_field_errors) {
978  if (form_field_errors.empty())
979    return;
980
981  for (std::vector<wallet::FormFieldError>::const_iterator it =
982           form_field_errors.begin();
983       it != form_field_errors.end(); ++it) {
984    if (it->error_type() == wallet::FormFieldError::UNKNOWN_ERROR ||
985        it->GetAutofillType() == MAX_VALID_FIELD_TYPE ||
986        it->location() == wallet::FormFieldError::UNKNOWN_LOCATION) {
987      wallet_server_validation_recoverable_ = false;
988      break;
989    }
990    DialogSection section = SectionFromLocation(it->location());
991    wallet_errors_[section][it->GetAutofillType()] =
992        std::make_pair(it->GetErrorMessage(),
993                       GetValueFromSection(section, it->GetAutofillType()));
994  }
995
996  // Unrecoverable validation errors.
997  if (!wallet_server_validation_recoverable_)
998    DisableWallet(wallet::WalletClient::UNKNOWN_ERROR);
999
1000  UpdateForErrors();
1001}
1002
1003void AutofillDialogControllerImpl::ConstructLegalDocumentsText() {
1004  legal_documents_text_.clear();
1005  legal_document_link_ranges_.clear();
1006
1007  if (!wallet_items_)
1008    return;
1009
1010  PrefService* local_state = g_browser_process->local_state();
1011  // List of users who have accepted location sharing for fraud protection
1012  // on this device.
1013  const base::ListValue* accepted =
1014      local_state->GetList(::prefs::kAutofillDialogWalletLocationAcceptance);
1015  bool has_accepted_location_sharing =
1016      accepted->Find(base::StringValue(
1017          account_chooser_model_.GetActiveWalletAccountName())) !=
1018      accepted->end();
1019
1020  if (wallet_items_->legal_documents().empty()) {
1021    if (!has_accepted_location_sharing) {
1022      legal_documents_text_ = l10n_util::GetStringUTF16(
1023          IDS_AUTOFILL_DIALOG_LOCATION_DISCLOSURE);
1024    }
1025
1026    return;
1027  }
1028
1029  const std::vector<wallet::WalletItems::LegalDocument*>& documents =
1030      wallet_items_->legal_documents();
1031  // There should never be just one document because the privacy policy doc gets
1032  // tacked on the end of other documents.
1033  DCHECK_GE(documents.size(), 2U);
1034
1035  std::vector<base::string16> link_names;
1036  for (size_t i = 0; i < documents.size(); ++i) {
1037    link_names.push_back(documents[i]->display_name());
1038  }
1039
1040  // TODO(estade): Replace this with a more permanent solution. See
1041  // http://crbug.com/319049 and http://crbug.com/316045
1042  const bool new_user = true;
1043  int resource_id = 0;
1044  switch (documents.size()) {
1045    case 2U:
1046      resource_id = new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_2 :
1047                               IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_2;
1048      break;
1049    case 3U:
1050      resource_id = new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_3 :
1051                               IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_3;
1052      break;
1053    case 4U:
1054      resource_id = new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_4 :
1055                               IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_4;
1056      break;
1057    case 5U:
1058      resource_id = new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_5 :
1059                               IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_5;
1060      break;
1061    case 6U:
1062      resource_id = new_user ? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_6 :
1063                               IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_6;
1064      break;
1065    default:
1066      // We can only handle so many documents. For lack of a better way of
1067      // handling document overflow, just error out if there are too many.
1068      DisableWallet(wallet::WalletClient::UNKNOWN_ERROR);
1069      return;
1070  }
1071
1072  std::vector<size_t> offsets;
1073  string16 text = l10n_util::GetStringFUTF16(resource_id, link_names, &offsets);
1074
1075  // Tack on the location string if need be.
1076  size_t base_offset = 0;
1077  if (!has_accepted_location_sharing) {
1078    text = l10n_util::GetStringFUTF16(
1079        IDS_AUTOFILL_DIALOG_LOCATION_DISCLOSURE_WITH_LEGAL_DOCS,
1080        text,
1081        &base_offset);
1082  }
1083
1084  for (size_t i = 0; i < documents.size(); ++i) {
1085    size_t link_start = offsets[i] + base_offset;
1086    legal_document_link_ranges_.push_back(gfx::Range(
1087        link_start, link_start + documents[i]->display_name().size()));
1088  }
1089  legal_documents_text_ = text;
1090}
1091
1092void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) {
1093  SetEditingExistingData(section, false);
1094
1095  DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1096  for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
1097    it->initial_value = common::GetHardcodedValueForType(it->type);
1098  }
1099}
1100
1101void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion(
1102    DialogSection section) {
1103  // |CreateWrapper()| returns an empty wrapper if |IsEditingExistingData()|, so
1104  // get the wrapper before this potentially happens below.
1105  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1106
1107  // If the chosen item in |model| yields an empty suggestion text, it is
1108  // invalid. In this case, show the edit UI and highlight invalid fields.
1109  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1110  string16 unused, unused2;
1111  if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) &&
1112      !SuggestionTextForSection(section, &unused, &unused2)) {
1113    SetEditingExistingData(section, true);
1114  }
1115
1116  DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1117  if (wrapper && IsEditingExistingData(section))
1118    wrapper->FillInputs(inputs);
1119}
1120
1121bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type,
1122                                                  const base::string16& value) {
1123  if (value.empty())
1124    return false;
1125
1126  // If this is a combobox at the default value, don't preserve it.
1127  ui::ComboboxModel* model = ComboboxModelForAutofillType(type);
1128  if (model && model->GetItemAt(model->GetDefaultIndex()) == value)
1129    return false;
1130
1131  return true;
1132}
1133
1134FieldValueMap AutofillDialogControllerImpl::TakeUserInputSnapshot() {
1135  FieldValueMap snapshot;
1136  if (!view_)
1137    return snapshot;
1138
1139  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1140    DialogSection section = static_cast<DialogSection>(i);
1141    SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1142    if (model->GetItemKeyForCheckedItem() != kAddNewItemKey)
1143      continue;
1144
1145    FieldValueMap outputs;
1146    view_->GetUserInput(section, &outputs);
1147    // Remove fields that are empty, at their default values, or invalid.
1148    for (FieldValueMap::iterator it = outputs.begin(); it != outputs.end();
1149         ++it) {
1150      if (InputWasEdited(it->first, it->second) &&
1151          InputValidityMessage(section, it->first, it->second).empty()) {
1152        snapshot.insert(std::make_pair(it->first, it->second));
1153      }
1154    }
1155  }
1156
1157  return snapshot;
1158}
1159
1160void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
1161    const FieldValueMap& snapshot) {
1162  if (snapshot.empty())
1163    return;
1164
1165  FieldMapWrapper wrapper(snapshot);
1166  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1167    DialogSection section = static_cast<DialogSection>(i);
1168    if (!SectionIsActive(section))
1169      continue;
1170
1171    DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1172    wrapper.FillInputs(inputs);
1173
1174    for (size_t i = 0; i < inputs->size(); ++i) {
1175      if (InputWasEdited((*inputs)[i].type, (*inputs)[i].initial_value)) {
1176        SuggestionsMenuModelForSection(section)->SetCheckedItem(kAddNewItemKey);
1177        break;
1178      }
1179    }
1180  }
1181}
1182
1183void AutofillDialogControllerImpl::UpdateSection(DialogSection section) {
1184  if (view_)
1185    view_->UpdateSection(section);
1186}
1187
1188void AutofillDialogControllerImpl::UpdateForErrors() {
1189  if (!view_)
1190    return;
1191
1192  // Currently, the view should only need to be updated if there are
1193  // |wallet_errors_| or validating a suggestion that's based on existing data.
1194  bool should_update = !wallet_errors_.empty();
1195  if (!should_update) {
1196    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1197      if (IsEditingExistingData(static_cast<DialogSection>(i))) {
1198        should_update = true;
1199        break;
1200      }
1201    }
1202  }
1203
1204  if (should_update)
1205    view_->UpdateForErrors();
1206}
1207
1208gfx::Image AutofillDialogControllerImpl::GetGeneratedCardImage(
1209    const base::string16& card_number,
1210    const base::string16& name,
1211    const SkColor& gradient_top,
1212    const SkColor& gradient_bottom) {
1213  const int kCardWidthPx = 300;
1214  const int kCardHeightPx = 190;
1215  const gfx::Size size(kCardWidthPx, kCardHeightPx);
1216  ui::ScaleFactor scale_factor = ui::GetScaleFactorForNativeView(
1217      web_contents()->GetView()->GetNativeView());
1218  gfx::Canvas canvas(size, ui::GetImageScale(scale_factor), false);
1219
1220  gfx::Rect display_rect(size);
1221
1222  skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
1223      0, size.height(), gradient_top, gradient_bottom);
1224  SkPaint paint;
1225  paint.setShader(shader.get());
1226  canvas.DrawRoundRect(display_rect, 8, paint);
1227
1228  display_rect.Inset(20, 0, 0, 0);
1229  gfx::Font font(l10n_util::GetStringUTF8(IDS_FIXED_FONT_FAMILY), 18);
1230  gfx::FontList font_list(font);
1231  gfx::ShadowValues shadows;
1232  shadows.push_back(gfx::ShadowValue(gfx::Point(0, 1), 1.0, SK_ColorBLACK));
1233  canvas.DrawStringRectWithShadows(
1234      card_number,
1235      font_list,
1236      SK_ColorWHITE,
1237      display_rect, 0, 0, shadows);
1238
1239  base::string16 capitalized_name = base::i18n::ToUpper(name);
1240  display_rect.Inset(0, size.height() / 2, 0, 0);
1241  canvas.DrawStringRectWithShadows(
1242      capitalized_name,
1243      font_list,
1244      SK_ColorWHITE,
1245      display_rect, 0, 0, shadows);
1246
1247  gfx::ImageSkia skia(canvas.ExtractImageRep());
1248  return gfx::Image(skia);
1249}
1250
1251void AutofillDialogControllerImpl::StartCardScramblingRefresher() {
1252  RefreshCardScramblingOverlay();
1253  card_scrambling_refresher_.Start(
1254      FROM_HERE,
1255      base::TimeDelta::FromMilliseconds(75),
1256      this,
1257      &AutofillDialogControllerImpl::RefreshCardScramblingOverlay);
1258}
1259
1260void AutofillDialogControllerImpl::RefreshCardScramblingOverlay() {
1261  scrambled_card_number_ = GenerateRandomCardNumber();
1262  PushOverlayUpdate();
1263}
1264
1265void AutofillDialogControllerImpl::PushOverlayUpdate() {
1266  if (view_) {
1267    ScopedViewUpdates updates(view_.get());
1268    view_->UpdateOverlay();
1269  }
1270}
1271
1272const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection(
1273    DialogSection section) const {
1274  switch (section) {
1275    case SECTION_CC:
1276      return requested_cc_fields_;
1277    case SECTION_BILLING:
1278      return requested_billing_fields_;
1279    case SECTION_CC_BILLING:
1280      return requested_cc_billing_fields_;
1281    case SECTION_SHIPPING:
1282      return requested_shipping_fields_;
1283  }
1284
1285  NOTREACHED();
1286  return requested_billing_fields_;
1287}
1288
1289ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
1290    ServerFieldType type) {
1291  switch (type) {
1292    case CREDIT_CARD_EXP_MONTH:
1293      return &cc_exp_month_combobox_model_;
1294
1295    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
1296      return &cc_exp_year_combobox_model_;
1297
1298    case ADDRESS_HOME_COUNTRY:
1299    case ADDRESS_BILLING_COUNTRY:
1300      return &country_combobox_model_;
1301
1302    default:
1303      return NULL;
1304  }
1305}
1306
1307ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSection(
1308    DialogSection section) {
1309  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1310  // The shipping section menu is special. It will always show because there is
1311  // a choice between "Use billing" and "enter new".
1312  if (section == SECTION_SHIPPING)
1313    return model;
1314
1315  // For other sections, only show a menu if there's at least one suggestion.
1316  for (int i = 0; i < model->GetItemCount(); ++i) {
1317    if (IsASuggestionItemKey(model->GetItemKeyAt(i)))
1318      return model;
1319  }
1320
1321  return NULL;
1322}
1323
1324ui::MenuModel* AutofillDialogControllerImpl::MenuModelForAccountChooser() {
1325  // If there were unrecoverable Wallet errors, or if there are choices other
1326  // than "Pay without the wallet", show the full menu.
1327  // TODO(estade): this can present a braindead menu (only 1 option) when
1328  // there's a wallet error.
1329  if (wallet_error_notification_ ||
1330      (SignedInState() == SIGNED_IN &&
1331       account_chooser_model_.HasAccountsToChoose() &&
1332       !ShouldShowSignInWebView())) {
1333    return &account_chooser_model_;
1334  }
1335
1336  // Otherwise, there is no menu, just a sign in link.
1337  return NULL;
1338}
1339
1340gfx::Image AutofillDialogControllerImpl::AccountChooserImage() {
1341  if (!MenuModelForAccountChooser() && !ShouldShowSignInWebView()) {
1342    return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1343        IDR_WALLET_ICON);
1344  }
1345
1346  return gfx::Image();
1347}
1348
1349gfx::Image AutofillDialogControllerImpl::ButtonStripImage() const {
1350  if (IsPayingWithWallet()) {
1351    return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1352        IDR_WALLET_LOGO);
1353  }
1354
1355  return gfx::Image();
1356}
1357
1358string16 AutofillDialogControllerImpl::LabelForSection(DialogSection section)
1359    const {
1360  switch (section) {
1361    case SECTION_CC:
1362      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC);
1363    case SECTION_BILLING:
1364    case SECTION_CC_BILLING:
1365      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING);
1366    case SECTION_SHIPPING:
1367      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING);
1368  }
1369  NOTREACHED();
1370  return string16();
1371}
1372
1373SuggestionState AutofillDialogControllerImpl::SuggestionStateForSection(
1374    DialogSection section) {
1375  string16 vertically_compact, horizontally_compact;
1376  bool show_suggestion = SuggestionTextForSection(section,
1377                                                  &vertically_compact,
1378                                                  &horizontally_compact);
1379  return SuggestionState(show_suggestion,
1380                         vertically_compact,
1381                         horizontally_compact,
1382                         SuggestionIconForSection(section),
1383                         ExtraSuggestionTextForSection(section),
1384                         ExtraSuggestionIconForSection(section));
1385}
1386
1387bool AutofillDialogControllerImpl::SuggestionTextForSection(
1388    DialogSection section,
1389    base::string16* vertically_compact,
1390    base::string16* horizontally_compact) {
1391  base::string16 action_text = RequiredActionTextForSection(section);
1392  if (!action_text.empty()) {
1393    *vertically_compact = *horizontally_compact = action_text;
1394    return true;
1395  }
1396
1397  // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a
1398  // user selects a credit card that has expired), don't show a suggestion (even
1399  // though there is a profile selected in the model).
1400  if (IsEditingExistingData(section))
1401    return false;
1402
1403  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1404  std::string item_key = model->GetItemKeyForCheckedItem();
1405  if (item_key == kSameAsBillingKey) {
1406    *vertically_compact = *horizontally_compact = l10n_util::GetStringUTF16(
1407        IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING);
1408    return true;
1409  }
1410
1411  if (!IsASuggestionItemKey(item_key))
1412    return false;
1413
1414  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1415  return wrapper->GetDisplayText(vertically_compact, horizontally_compact);
1416}
1417
1418string16 AutofillDialogControllerImpl::RequiredActionTextForSection(
1419    DialogSection section) const {
1420  if (section == SECTION_CC_BILLING && IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1421    const wallet::WalletItems::MaskedInstrument* current_instrument =
1422        wallet_items_->GetInstrumentById(active_instrument_id_);
1423    if (current_instrument)
1424      return current_instrument->TypeAndLastFourDigits();
1425
1426    FieldValueMap output;
1427    view_->GetUserInput(section, &output);
1428    CreditCard card;
1429    GetBillingInfoFromOutputs(output, &card, NULL, NULL);
1430    return card.TypeAndLastFourDigits();
1431  }
1432
1433  return string16();
1434}
1435
1436string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
1437    DialogSection section) const {
1438  if (section == SECTION_CC ||
1439      (section == SECTION_CC_BILLING && IsSubmitPausedOn(wallet::VERIFY_CVV))) {
1440    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC);
1441  }
1442
1443  return string16();
1444}
1445
1446const wallet::WalletItems::MaskedInstrument* AutofillDialogControllerImpl::
1447    ActiveInstrument() const {
1448  if (!IsPayingWithWallet())
1449    return NULL;
1450
1451  const SuggestionsMenuModel* model =
1452      SuggestionsMenuModelForSection(SECTION_CC_BILLING);
1453  const std::string item_key = model->GetItemKeyForCheckedItem();
1454  if (!IsASuggestionItemKey(item_key))
1455    return NULL;
1456
1457  int index;
1458  if (!base::StringToInt(item_key, &index) || index < 0 ||
1459      static_cast<size_t>(index) >= wallet_items_->instruments().size()) {
1460    NOTREACHED();
1461    return NULL;
1462  }
1463
1464  return wallet_items_->instruments()[index];
1465}
1466
1467const wallet::Address* AutofillDialogControllerImpl::
1468    ActiveShippingAddress() const {
1469  if (!IsPayingWithWallet() || !IsShippingAddressRequired())
1470    return NULL;
1471
1472  const SuggestionsMenuModel* model =
1473      SuggestionsMenuModelForSection(SECTION_SHIPPING);
1474  const std::string item_key = model->GetItemKeyForCheckedItem();
1475  if (!IsASuggestionItemKey(item_key))
1476    return NULL;
1477
1478  int index;
1479  if (!base::StringToInt(item_key, &index) || index < 0 ||
1480      static_cast<size_t>(index) >= wallet_items_->addresses().size()) {
1481    NOTREACHED();
1482    return NULL;
1483  }
1484
1485  return wallet_items_->addresses()[index];
1486}
1487
1488scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
1489    DialogSection section) {
1490  if (IsPayingWithWallet() && full_wallet_ &&
1491      full_wallet_->required_actions().empty()) {
1492    if (section == SECTION_CC_BILLING) {
1493      return scoped_ptr<DataModelWrapper>(
1494          new FullWalletBillingWrapper(full_wallet_.get()));
1495    }
1496    if (section == SECTION_SHIPPING) {
1497      return scoped_ptr<DataModelWrapper>(
1498          new FullWalletShippingWrapper(full_wallet_.get()));
1499    }
1500  }
1501
1502  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1503  std::string item_key = model->GetItemKeyForCheckedItem();
1504  if (!IsASuggestionItemKey(item_key) || IsManuallyEditingSection(section))
1505    return scoped_ptr<DataModelWrapper>();
1506
1507  if (IsPayingWithWallet()) {
1508    if (section == SECTION_CC_BILLING) {
1509      return scoped_ptr<DataModelWrapper>(
1510          new WalletInstrumentWrapper(ActiveInstrument()));
1511    }
1512
1513    if (section == SECTION_SHIPPING) {
1514      return scoped_ptr<DataModelWrapper>(
1515          new WalletAddressWrapper(ActiveShippingAddress()));
1516    }
1517
1518    return scoped_ptr<DataModelWrapper>();
1519  }
1520
1521  if (section == SECTION_CC) {
1522    CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
1523    DCHECK(card);
1524    return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card));
1525  }
1526
1527  AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
1528  DCHECK(profile);
1529  if (section == SECTION_SHIPPING) {
1530    return scoped_ptr<DataModelWrapper>(
1531        new AutofillShippingAddressWrapper(profile));
1532  }
1533  DCHECK_EQ(SECTION_BILLING, section);
1534  return scoped_ptr<DataModelWrapper>(
1535      new AutofillProfileWrapper(profile));
1536}
1537
1538gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
1539    DialogSection section) {
1540  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
1541  if (!model.get())
1542    return gfx::Image();
1543
1544  return model->GetIcon();
1545}
1546
1547gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
1548    DialogSection section) {
1549  if (section != SECTION_CC && section != SECTION_CC_BILLING)
1550    return gfx::Image();
1551
1552  scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
1553  if (!model.get())
1554    return gfx::Image();
1555
1556  return CvcIconForCreditCardType(
1557      model->GetInfo(AutofillType(CREDIT_CARD_TYPE)));
1558}
1559
1560FieldIconMap AutofillDialogControllerImpl::IconsForFields(
1561    const FieldValueMap& user_inputs) const {
1562  FieldIconMap result;
1563  base::string16 credit_card_type;
1564
1565  FieldValueMap::const_iterator credit_card_iter =
1566      user_inputs.find(CREDIT_CARD_NUMBER);
1567  if (credit_card_iter != user_inputs.end()) {
1568    const string16& number = credit_card_iter->second;
1569    const std::string type = CreditCard::GetCreditCardType(number);
1570    credit_card_type = CreditCard::TypeForDisplay(type);
1571    result[CREDIT_CARD_NUMBER] = CreditCardIconForType(type);
1572  }
1573
1574  if (!user_inputs.count(CREDIT_CARD_VERIFICATION_CODE))
1575    return result;
1576
1577  result[CREDIT_CARD_VERIFICATION_CODE] =
1578      CvcIconForCreditCardType(credit_card_type);
1579
1580  return result;
1581}
1582
1583bool AutofillDialogControllerImpl::FieldControlsIcons(
1584    ServerFieldType type) const {
1585  return type == CREDIT_CARD_NUMBER;
1586}
1587
1588string16 AutofillDialogControllerImpl::TooltipForField(ServerFieldType type)
1589    const {
1590  if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER)
1591    return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TOOLTIP_PHONE_NUMBER);
1592
1593  return string16();
1594}
1595
1596bool AutofillDialogControllerImpl::InputIsEditable(
1597    const DetailInput& input,
1598    DialogSection section) {
1599  if (section != SECTION_CC_BILLING)
1600    return true;
1601
1602  if (input.type == CREDIT_CARD_NUMBER)
1603    return !IsEditingExistingData(section);
1604
1605  // For CVC, only require (allow) input if the user has edited some other
1606  // aspect of the card.
1607  if (input.type == CREDIT_CARD_VERIFICATION_CODE &&
1608      IsEditingExistingData(section)) {
1609    FieldValueMap output;
1610    view_->GetUserInput(section, &output);
1611    WalletInstrumentWrapper wrapper(ActiveInstrument());
1612
1613    for (FieldValueMap::iterator iter = output.begin(); iter != output.end();
1614         ++iter) {
1615      if (iter->first == input.type)
1616        continue;
1617
1618      AutofillType type(iter->first);
1619      if (type.group() == CREDIT_CARD &&
1620          iter->second != wrapper.GetInfo(type)) {
1621        return true;
1622      }
1623    }
1624
1625    return false;
1626  }
1627
1628  return true;
1629}
1630
1631// TODO(groby): Add more tests.
1632string16 AutofillDialogControllerImpl::InputValidityMessage(
1633    DialogSection section,
1634    ServerFieldType type,
1635    const string16& value) {
1636  // If the field is edited, clear any Wallet errors.
1637  if (IsPayingWithWallet()) {
1638    WalletValidationErrors::iterator it = wallet_errors_.find(section);
1639    if (it != wallet_errors_.end()) {
1640      TypeErrorInputMap::const_iterator iter = it->second.find(type);
1641      if (iter != it->second.end()) {
1642        if (iter->second.second == value)
1643          return iter->second.first;
1644        it->second.erase(type);
1645      }
1646    }
1647  }
1648
1649  switch (AutofillType(type).GetStorableType()) {
1650    case EMAIL_ADDRESS:
1651      if (!value.empty() && !IsValidEmailAddress(value)) {
1652        return l10n_util::GetStringUTF16(
1653            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS);
1654      }
1655      break;
1656
1657    case CREDIT_CARD_NUMBER: {
1658      if (!value.empty()) {
1659        base::string16 message = CreditCardNumberValidityMessage(value);
1660        if (!message.empty())
1661          return message;
1662      }
1663      break;
1664    }
1665
1666    case CREDIT_CARD_EXP_MONTH:
1667      if (!InputWasEdited(CREDIT_CARD_EXP_MONTH, value)) {
1668        return l10n_util::GetStringUTF16(
1669            IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE);
1670      }
1671      break;
1672
1673    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
1674      if (!InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR, value)) {
1675        return l10n_util::GetStringUTF16(
1676            IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE);
1677      }
1678      break;
1679
1680    case CREDIT_CARD_VERIFICATION_CODE:
1681      if (!value.empty() && !autofill::IsValidCreditCardSecurityCode(value)) {
1682        return l10n_util::GetStringUTF16(
1683            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1684      }
1685      break;
1686
1687    case ADDRESS_HOME_LINE1:
1688      break;
1689
1690    case ADDRESS_HOME_LINE2:
1691      return base::string16();  // Line 2 is optional - always valid.
1692
1693    case ADDRESS_HOME_CITY:
1694    case ADDRESS_HOME_COUNTRY:
1695      break;
1696
1697    case ADDRESS_HOME_STATE:
1698      if (!value.empty() && !autofill::IsValidState(value)) {
1699        return l10n_util::GetStringUTF16(
1700            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_REGION);
1701      }
1702      break;
1703
1704    case ADDRESS_HOME_ZIP:
1705      if (!value.empty() && !autofill::IsValidZip(value)) {
1706        return l10n_util::GetStringUTF16(
1707            IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_ZIP_CODE);
1708      }
1709      break;
1710
1711    case NAME_FULL:
1712      // Wallet requires a first and last billing name.
1713      if (section == SECTION_CC_BILLING && !value.empty() &&
1714          !IsCardHolderNameValidForWallet(value)) {
1715        DCHECK(IsPayingWithWallet());
1716        return l10n_util::GetStringUTF16(
1717            IDS_AUTOFILL_DIALOG_VALIDATION_WALLET_REQUIRES_TWO_NAMES);
1718      }
1719      break;
1720
1721    case PHONE_HOME_WHOLE_NUMBER:  // Used in shipping section.
1722      break;
1723
1724    case PHONE_BILLING_WHOLE_NUMBER:  // Used in billing section.
1725      break;
1726
1727    default:
1728      NOTREACHED();  // Trying to validate unknown field.
1729      break;
1730  }
1731
1732  return value.empty() ?
1733      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE) :
1734      base::string16();
1735}
1736
1737// TODO(groby): Also add tests.
1738ValidityMessages AutofillDialogControllerImpl::InputsAreValid(
1739    DialogSection section,
1740    const FieldValueMap& inputs) {
1741  ValidityMessages messages;
1742  std::map<ServerFieldType, string16> field_values;
1743  for (FieldValueMap::const_iterator iter = inputs.begin();
1744       iter != inputs.end(); ++iter) {
1745    const ServerFieldType type = iter->first;
1746
1747    base::string16 text = InputValidityMessage(section, type, iter->second);
1748
1749    // Skip empty/unchanged fields in edit mode. Ignore country code as it
1750    // always has a value. If the individual field does not have validation
1751    // errors, assume it to be valid unless later proven otherwise.
1752    bool sure = InputWasEdited(type, iter->second) ||
1753                ComboboxModelForAutofillType(type) == &country_combobox_model_;
1754
1755    // Consider only individually valid fields for inter-field validation.
1756    if (text.empty()) {
1757      field_values[type] = iter->second;
1758      // If the field is valid but can be invalidated by inter-field validation,
1759      // assume it to be unsure.
1760      if (type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
1761          type == CREDIT_CARD_EXP_MONTH ||
1762          type == CREDIT_CARD_VERIFICATION_CODE ||
1763          type == PHONE_HOME_WHOLE_NUMBER ||
1764          type == PHONE_BILLING_WHOLE_NUMBER) {
1765        sure = false;
1766      }
1767    }
1768    messages.Set(type, ValidityMessage(text, sure));
1769  }
1770
1771  // Validate the date formed by month and year field. (Autofill dialog is
1772  // never supposed to have 2-digit years, so not checked).
1773  if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
1774      field_values.count(CREDIT_CARD_EXP_MONTH) &&
1775      InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR,
1776                     field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR]) &&
1777      InputWasEdited(CREDIT_CARD_EXP_MONTH,
1778                     field_values[CREDIT_CARD_EXP_MONTH])) {
1779    ValidityMessage year_message(base::string16(), true);
1780    ValidityMessage month_message(base::string16(), true);
1781    if (!IsCreditCardExpirationValid(field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
1782                                     field_values[CREDIT_CARD_EXP_MONTH])) {
1783      // The dialog shows the same error message for the month and year fields.
1784      year_message.text = l10n_util::GetStringUTF16(
1785          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1786      month_message.text = l10n_util::GetStringUTF16(
1787          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1788    }
1789    messages.Set(CREDIT_CARD_EXP_4_DIGIT_YEAR, year_message);
1790    messages.Set(CREDIT_CARD_EXP_MONTH, month_message);
1791  }
1792
1793  // If there is a credit card number and a CVC, validate them together.
1794  if (field_values.count(CREDIT_CARD_NUMBER) &&
1795      field_values.count(CREDIT_CARD_VERIFICATION_CODE)) {
1796    ValidityMessage ccv_message(base::string16(), true);
1797    if (!autofill::IsValidCreditCardSecurityCode(
1798            field_values[CREDIT_CARD_VERIFICATION_CODE],
1799            field_values[CREDIT_CARD_NUMBER])) {
1800      ccv_message.text = l10n_util::GetStringUTF16(
1801          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1802    }
1803    messages.Set(CREDIT_CARD_VERIFICATION_CODE, ccv_message);
1804  }
1805
1806  // Validate the shipping phone number against the country code of the address.
1807  if (field_values.count(ADDRESS_HOME_COUNTRY) &&
1808      field_values.count(PHONE_HOME_WHOLE_NUMBER)) {
1809    i18n::PhoneObject phone_object(
1810        field_values[PHONE_HOME_WHOLE_NUMBER],
1811        AutofillCountry::GetCountryCode(
1812            field_values[ADDRESS_HOME_COUNTRY],
1813            g_browser_process->GetApplicationLocale()));
1814    ValidityMessage phone_message(base::string16(), true);
1815    if (!phone_object.IsValidNumber()) {
1816      phone_message.text = l10n_util::GetStringUTF16(
1817          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1818    }
1819    messages.Set(PHONE_HOME_WHOLE_NUMBER, phone_message);
1820  }
1821
1822  // Validate the billing phone number against the country code of the address.
1823  if (field_values.count(ADDRESS_BILLING_COUNTRY) &&
1824      field_values.count(PHONE_BILLING_WHOLE_NUMBER)) {
1825    i18n::PhoneObject phone_object(
1826        field_values[PHONE_BILLING_WHOLE_NUMBER],
1827        AutofillCountry::GetCountryCode(
1828            field_values[ADDRESS_BILLING_COUNTRY],
1829            g_browser_process->GetApplicationLocale()));
1830    ValidityMessage phone_message(base::string16(), true);
1831    if (!phone_object.IsValidNumber()) {
1832      phone_message.text = l10n_util::GetStringUTF16(
1833          IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
1834    }
1835    messages.Set(PHONE_BILLING_WHOLE_NUMBER, phone_message);
1836  }
1837
1838  return messages;
1839}
1840
1841void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1842    DialogSection section,
1843    ServerFieldType type,
1844    gfx::NativeView parent_view,
1845    const gfx::Rect& content_bounds,
1846    const string16& field_contents,
1847    bool was_edit) {
1848  // If the field is edited down to empty, don't show a popup.
1849  if (was_edit && field_contents.empty()) {
1850    HidePopup();
1851    return;
1852  }
1853
1854  // If the user clicks while the popup is already showing, be sure to hide
1855  // it.
1856  if (!was_edit && popup_controller_.get()) {
1857    HidePopup();
1858    return;
1859  }
1860
1861  std::vector<string16> popup_values, popup_labels, popup_icons;
1862  if (common::IsCreditCardType(type)) {
1863    GetManager()->GetCreditCardSuggestions(AutofillType(type),
1864                                           field_contents,
1865                                           &popup_values,
1866                                           &popup_labels,
1867                                           &popup_icons,
1868                                           &popup_guids_);
1869  } else {
1870    std::vector<ServerFieldType> field_types;
1871    const DetailInputs& inputs = RequestedFieldsForSection(section);
1872    for (DetailInputs::const_iterator iter = inputs.begin();
1873         iter != inputs.end(); ++iter) {
1874      field_types.push_back(iter->type);
1875    }
1876    GetManager()->GetProfileSuggestions(AutofillType(type),
1877                                        field_contents,
1878                                        false,
1879                                        field_types,
1880                                        &popup_values,
1881                                        &popup_labels,
1882                                        &popup_icons,
1883                                        &popup_guids_);
1884  }
1885
1886  if (popup_values.empty()) {
1887    HidePopup();
1888    return;
1889  }
1890
1891  // |input_showing_popup_| must be set before calling |Show()|.
1892  const DetailInputs& inputs = RequestedFieldsForSection(section);
1893  for (DetailInputs::const_iterator iter = inputs.begin();
1894       iter != inputs.end(); ++iter) {
1895    if (iter->type == type) {
1896      input_showing_popup_ = &(*iter);
1897      break;
1898    }
1899  }
1900
1901  if (!input_showing_popup_)
1902    return;
1903
1904  // TODO(estade): do we need separators and control rows like 'Clear
1905  // Form'?
1906  std::vector<int> popup_ids;
1907  for (size_t i = 0; i < popup_guids_.size(); ++i) {
1908    popup_ids.push_back(i);
1909  }
1910
1911  popup_controller_ = AutofillPopupControllerImpl::GetOrCreate(
1912      popup_controller_,
1913      weak_ptr_factory_.GetWeakPtr(),
1914      NULL,
1915      parent_view,
1916      content_bounds,
1917      base::i18n::IsRTL() ?
1918          base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT);
1919  popup_controller_->set_hide_on_outside_click(true);
1920  popup_controller_->Show(popup_values,
1921                          popup_labels,
1922                          popup_icons,
1923                          popup_ids);
1924}
1925
1926void AutofillDialogControllerImpl::FocusMoved() {
1927  HidePopup();
1928}
1929
1930bool AutofillDialogControllerImpl::ShouldShowErrorBubble() const {
1931  return !input_showing_popup_;
1932}
1933
1934void AutofillDialogControllerImpl::ViewClosed() {
1935  GetManager()->RemoveObserver(this);
1936
1937  // Called from here rather than in ~AutofillDialogControllerImpl as this
1938  // relies on virtual methods that change to their base class in the dtor.
1939  MaybeShowCreditCardBubble();
1940
1941  delete this;
1942}
1943
1944std::vector<DialogNotification> AutofillDialogControllerImpl::
1945    CurrentNotifications() {
1946  std::vector<DialogNotification> notifications;
1947
1948  // TODO(dbeam): figure out a way to dismiss this error after a while.
1949  if (wallet_error_notification_)
1950    notifications.push_back(*wallet_error_notification_);
1951
1952  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
1953    notifications.push_back(DialogNotification(
1954        DialogNotification::REQUIRED_ACTION,
1955        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VERIFY_CVV)));
1956  }
1957
1958  if (!wallet_server_validation_recoverable_) {
1959    notifications.push_back(DialogNotification(
1960        DialogNotification::REQUIRED_ACTION,
1961        l10n_util::GetStringUTF16(
1962            IDS_AUTOFILL_DIALOG_FAILED_TO_SAVE_WALLET_DATA)));
1963  }
1964
1965  if (choose_another_instrument_or_address_) {
1966    notifications.push_back(DialogNotification(
1967        DialogNotification::REQUIRED_ACTION,
1968        l10n_util::GetStringUTF16(
1969            IDS_AUTOFILL_DIALOG_CHOOSE_DIFFERENT_WALLET_INSTRUMENT)));
1970  }
1971
1972  if (notifications.empty() && MenuModelForAccountChooser()) {
1973    base::string16 text = l10n_util::GetStringUTF16(
1974        IsManuallyEditingAnySection() ?
1975            IDS_AUTOFILL_DIALOG_SAVE_DETAILS_IN_WALLET :
1976            IDS_AUTOFILL_DIALOG_USE_WALLET);
1977    DialogNotification notification(
1978        DialogNotification::WALLET_USAGE_CONFIRMATION,
1979        text);
1980    notification.set_tooltip_text(
1981        l10n_util::GetStringUTF16(
1982            IDS_AUTOFILL_DIALOG_SAVE_IN_WALLET_TOOLTIP));
1983    notification.set_checked(IsPayingWithWallet());
1984    notifications.push_back(notification);
1985  }
1986
1987  if (IsPayingWithWallet() && !wallet::IsUsingProd()) {
1988    notifications.push_back(DialogNotification(
1989        DialogNotification::DEVELOPER_WARNING,
1990        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_NOT_PROD_WARNING)));
1991  }
1992
1993  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
1994    notifications.push_back(DialogNotification(
1995        DialogNotification::SECURITY_WARNING,
1996        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECURITY_WARNING)));
1997  }
1998
1999  if (!invoked_from_same_origin_) {
2000    notifications.push_back(DialogNotification(
2001        DialogNotification::SECURITY_WARNING,
2002        l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING,
2003                                   UTF8ToUTF16(source_url_.host()))));
2004  }
2005
2006  return notifications;
2007}
2008
2009void AutofillDialogControllerImpl::LinkClicked(const GURL& url) {
2010  OpenTabWithUrl(url);
2011}
2012
2013void AutofillDialogControllerImpl::SignInLinkClicked() {
2014  ScopedViewUpdates updates(view_.get());
2015
2016  if (SignedInState() == NOT_CHECKED) {
2017    handling_use_wallet_link_click_ = true;
2018    account_chooser_model_.SelectWalletAccount(0);
2019    FetchWalletCookie();
2020    view_->UpdateAccountChooser();
2021  } else if (signin_registrar_.IsEmpty()) {
2022    // Start sign in.
2023    waiting_for_explicit_sign_in_response_ = true;
2024    content::Source<content::NavigationController> source(view_->ShowSignIn());
2025    signin_registrar_.Add(
2026        this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
2027    view_->UpdateAccountChooser();
2028
2029    GetMetricLogger().LogDialogUiEvent(
2030        AutofillMetrics::DIALOG_UI_SIGNIN_SHOWN);
2031  } else {
2032    waiting_for_explicit_sign_in_response_ = false;
2033    HideSignIn();
2034  }
2035}
2036
2037void AutofillDialogControllerImpl::NotificationCheckboxStateChanged(
2038    DialogNotification::Type type, bool checked) {
2039  if (type == DialogNotification::WALLET_USAGE_CONFIRMATION) {
2040    if (checked) {
2041      account_chooser_model_.SelectWalletAccount(
2042          GetWalletClient()->user_index());
2043    } else {
2044      account_chooser_model_.SelectUseAutofill();
2045    }
2046
2047    AccountChoiceChanged();
2048  }
2049}
2050
2051void AutofillDialogControllerImpl::LegalDocumentLinkClicked(
2052    const gfx::Range& range) {
2053  for (size_t i = 0; i < legal_document_link_ranges_.size(); ++i) {
2054    if (legal_document_link_ranges_[i] == range) {
2055      OpenTabWithUrl(wallet_items_->legal_documents()[i]->url());
2056      return;
2057    }
2058  }
2059
2060  NOTREACHED();
2061}
2062
2063bool AutofillDialogControllerImpl::OnCancel() {
2064  HidePopup();
2065  if (!is_submitting_)
2066    LogOnCancelMetrics();
2067  callback_.Run(NULL);
2068  return true;
2069}
2070
2071bool AutofillDialogControllerImpl::OnAccept() {
2072  ScopedViewUpdates updates(view_.get());
2073  choose_another_instrument_or_address_ = false;
2074  wallet_server_validation_recoverable_ = true;
2075  HidePopup();
2076
2077  // This must come before SetIsSubmitting().
2078  if (IsPayingWithWallet()) {
2079    // In the VERIFY_CVV case, hold onto the previously submitted cardholder
2080    // name.
2081    if (!IsSubmitPausedOn(wallet::VERIFY_CVV)) {
2082      submitted_cardholder_name_ =
2083          GetValueFromSection(SECTION_CC_BILLING, NAME_BILLING_FULL);
2084
2085      // Snag the last four digits of the backing card now as it could be wiped
2086      // out if a CVC challenge happens.
2087      if (ActiveInstrument()) {
2088        backing_card_last_four_ = ActiveInstrument()->TypeAndLastFourDigits();
2089      } else {
2090        FieldValueMap output;
2091        view_->GetUserInput(SECTION_CC_BILLING, &output);
2092        CreditCard card;
2093        GetBillingInfoFromOutputs(output, &card, NULL, NULL);
2094        backing_card_last_four_ = card.TypeAndLastFourDigits();
2095      }
2096    }
2097    DCHECK(!submitted_cardholder_name_.empty());
2098    DCHECK(!backing_card_last_four_.empty());
2099  }
2100
2101  SetIsSubmitting(true);
2102
2103  if (IsSubmitPausedOn(wallet::VERIFY_CVV)) {
2104    DCHECK(!active_instrument_id_.empty());
2105    full_wallet_.reset();
2106    GetWalletClient()->AuthenticateInstrument(
2107        active_instrument_id_,
2108        UTF16ToUTF8(view_->GetCvc()));
2109    view_->UpdateOverlay();
2110  } else if (IsPayingWithWallet()) {
2111    AcceptLegalTerms();
2112  } else {
2113    FinishSubmit();
2114  }
2115
2116  return false;
2117}
2118
2119Profile* AutofillDialogControllerImpl::profile() {
2120  return profile_;
2121}
2122
2123content::WebContents* AutofillDialogControllerImpl::GetWebContents() {
2124  return web_contents();
2125}
2126
2127////////////////////////////////////////////////////////////////////////////////
2128// AutofillPopupDelegate implementation.
2129
2130void AutofillDialogControllerImpl::OnPopupShown() {
2131  ScopedViewUpdates update(view_.get());
2132  view_->UpdateErrorBubble();
2133
2134  GetMetricLogger().LogDialogPopupEvent(AutofillMetrics::DIALOG_POPUP_SHOWN);
2135}
2136
2137void AutofillDialogControllerImpl::OnPopupHidden() {}
2138
2139bool AutofillDialogControllerImpl::ShouldRepostEvent(
2140    const ui::MouseEvent& event) {
2141  // If the event would be reposted inside |input_showing_popup_|, just ignore.
2142  return !view_->HitTestInput(*input_showing_popup_, event.location());
2143}
2144
2145void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier) {
2146  // TODO(estade): implement.
2147}
2148
2149void AutofillDialogControllerImpl::DidAcceptSuggestion(const string16& value,
2150                                                       int identifier) {
2151  ScopedViewUpdates updates(view_.get());
2152  const PersonalDataManager::GUIDPair& pair = popup_guids_[identifier];
2153
2154  scoped_ptr<DataModelWrapper> wrapper;
2155  if (common::IsCreditCardType(input_showing_popup_->type)) {
2156    wrapper.reset(new AutofillCreditCardWrapper(
2157        GetManager()->GetCreditCardByGUID(pair.first)));
2158  } else {
2159    wrapper.reset(new AutofillProfileWrapper(
2160        GetManager()->GetProfileByGUID(pair.first),
2161        AutofillType(input_showing_popup_->type),
2162        pair.second));
2163  }
2164
2165  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2166    DialogSection section = static_cast<DialogSection>(i);
2167    wrapper->FillInputs(MutableRequestedFieldsForSection(section));
2168    view_->FillSection(section, *input_showing_popup_);
2169  }
2170
2171  GetMetricLogger().LogDialogPopupEvent(
2172      AutofillMetrics::DIALOG_POPUP_FORM_FILLED);
2173
2174  // TODO(estade): not sure why it's necessary to do this explicitly.
2175  HidePopup();
2176}
2177
2178void AutofillDialogControllerImpl::RemoveSuggestion(const string16& value,
2179                                                    int identifier) {
2180  // TODO(estade): implement.
2181}
2182
2183void AutofillDialogControllerImpl::ClearPreviewedForm() {
2184  // TODO(estade): implement.
2185}
2186
2187////////////////////////////////////////////////////////////////////////////////
2188// content::NotificationObserver implementation.
2189
2190void AutofillDialogControllerImpl::Observe(
2191    int type,
2192    const content::NotificationSource& source,
2193    const content::NotificationDetails& details) {
2194  DCHECK_EQ(type, content::NOTIFICATION_NAV_ENTRY_COMMITTED);
2195  content::LoadCommittedDetails* load_details =
2196      content::Details<content::LoadCommittedDetails>(details).ptr();
2197  size_t user_index = 0;
2198  if (IsSignInContinueUrl(load_details->entry->GetVirtualURL(), &user_index)) {
2199    GetWalletClient()->set_user_index(user_index);
2200    FetchWalletCookie();
2201
2202    // NOTE: |HideSignIn()| may delete the WebContents which doesn't expect to
2203    // be deleted while committing a nav entry. Just call |HideSignIn()| later.
2204    base::MessageLoop::current()->PostTask(FROM_HERE,
2205        base::Bind(&AutofillDialogControllerImpl::HideSignIn,
2206                   base::Unretained(this)));
2207  }
2208}
2209
2210////////////////////////////////////////////////////////////////////////////////
2211// SuggestionsMenuModelDelegate implementation.
2212
2213void AutofillDialogControllerImpl::SuggestionsMenuWillShow() {
2214  HidePopup();
2215}
2216
2217void AutofillDialogControllerImpl::SuggestionItemSelected(
2218    SuggestionsMenuModel* model,
2219    size_t index) {
2220  ScopedViewUpdates updates(view_.get());
2221
2222  if (model->GetItemKeyAt(index) == kManageItemsKey) {
2223    GURL url;
2224    if (!IsPayingWithWallet()) {
2225      GURL settings_url(chrome::kChromeUISettingsURL);
2226      url = settings_url.Resolve(chrome::kAutofillSubPage);
2227    } else {
2228      // Reset |last_wallet_items_fetch_timestamp_| to ensure that the Wallet
2229      // data is refreshed as soon as the user switches back to this tab after
2230      // potentially editing his data.
2231      last_wallet_items_fetch_timestamp_ = base::TimeTicks();
2232      size_t user_index = GetWalletClient()->user_index();
2233      url = SectionForSuggestionsMenuModel(*model) == SECTION_SHIPPING ?
2234          wallet::GetManageAddressesUrl(user_index) :
2235          wallet::GetManageInstrumentsUrl(user_index);
2236    }
2237
2238    OpenTabWithUrl(url);
2239    return;
2240  }
2241
2242  model->SetCheckedIndex(index);
2243  DialogSection section = SectionForSuggestionsMenuModel(*model);
2244  ResetSectionInput(section);
2245  ShowEditUiIfBadSuggestion(section);
2246  UpdateSection(section);
2247  view_->UpdateNotificationArea();
2248  UpdateForErrors();
2249
2250  LogSuggestionItemSelectedMetric(*model);
2251}
2252
2253////////////////////////////////////////////////////////////////////////////////
2254// wallet::WalletClientDelegate implementation.
2255
2256const AutofillMetrics& AutofillDialogControllerImpl::GetMetricLogger() const {
2257  return metric_logger_;
2258}
2259
2260std::string AutofillDialogControllerImpl::GetRiskData() const {
2261  DCHECK(!risk_data_.empty());
2262  return risk_data_;
2263}
2264
2265std::string AutofillDialogControllerImpl::GetWalletCookieValue() const {
2266  return wallet_cookie_value_;
2267}
2268
2269bool AutofillDialogControllerImpl::IsShippingAddressRequired() const {
2270  return cares_about_shipping_;
2271}
2272
2273void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
2274  DCHECK(is_submitting_ && IsPayingWithWallet());
2275  has_accepted_legal_documents_ = true;
2276  LoadRiskFingerprintData();
2277}
2278
2279void AutofillDialogControllerImpl::OnDidAuthenticateInstrument(bool success) {
2280  DCHECK(is_submitting_ && IsPayingWithWallet());
2281
2282  // TODO(dbeam): use the returned full wallet. http://crbug.com/224992
2283  if (success) {
2284    GetFullWallet();
2285  } else {
2286    DisableWallet(wallet::WalletClient::UNKNOWN_ERROR);
2287    SuggestionsUpdated();
2288  }
2289}
2290
2291void AutofillDialogControllerImpl::OnDidGetFullWallet(
2292    scoped_ptr<wallet::FullWallet> full_wallet) {
2293  DCHECK(is_submitting_ && IsPayingWithWallet());
2294  ScopedViewUpdates updates(view_.get());
2295
2296  full_wallet_ = full_wallet.Pass();
2297
2298  if (full_wallet_->required_actions().empty()) {
2299    FinishSubmit();
2300    return;
2301  }
2302
2303  switch (full_wallet_->required_actions()[0]) {
2304    case wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS:
2305      choose_another_instrument_or_address_ = true;
2306      SetIsSubmitting(false);
2307      GetWalletItems();
2308      break;
2309
2310    case wallet::VERIFY_CVV:
2311      SuggestionsUpdated();
2312      break;
2313
2314    default:
2315      DisableWallet(wallet::WalletClient::UNKNOWN_ERROR);
2316      return;
2317  }
2318
2319  view_->UpdateNotificationArea();
2320  view_->UpdateButtonStrip();
2321  view_->UpdateOverlay();
2322}
2323
2324void AutofillDialogControllerImpl::OnPassiveSigninSuccess() {
2325  FetchWalletCookie();
2326}
2327
2328void AutofillDialogControllerImpl::OnPassiveSigninFailure(
2329    const GoogleServiceAuthError& error) {
2330  signin_helper_.reset();
2331  passive_failed_ = true;
2332
2333  if (handling_use_wallet_link_click_ ||
2334      GetWalletClient()->user_index() != 0) {
2335    // TODO(estade): When a secondary account is selected and fails passive
2336    // auth, we show a sign in page. Currently we show the generic add account
2337    // page, but we should instead show sign in for the selected account.
2338    // http://crbug.com/323327
2339    SignInLinkClicked();
2340    handling_use_wallet_link_click_ = false;
2341  }
2342
2343  OnWalletSigninError();
2344}
2345
2346void AutofillDialogControllerImpl::OnDidFetchWalletCookieValue(
2347    const std::string& cookie_value) {
2348  wallet_cookie_value_ = cookie_value;
2349  signin_helper_.reset();
2350  GetWalletItems();
2351}
2352
2353void AutofillDialogControllerImpl::OnDidGetWalletItems(
2354    scoped_ptr<wallet::WalletItems> wallet_items) {
2355  legal_documents_text_.clear();
2356  legal_document_link_ranges_.clear();
2357  has_accepted_legal_documents_ = false;
2358
2359  wallet_items_ = wallet_items.Pass();
2360
2361  if (wallet_items_) {
2362    // Making sure the user index is in sync shouldn't be necessary, but is an
2363    // extra precaution. But if there is no active account (such as in the
2364    // PASSIVE_AUTH case), stick with the old active account.
2365    if (!wallet_items_->obfuscated_gaia_id().empty())
2366      GetWalletClient()->set_user_index(wallet_items_->active_account_index());
2367
2368    account_chooser_model_.SetWalletAccounts(
2369        wallet_items_->gaia_accounts(),
2370        wallet_items_->active_account_index());
2371  }
2372
2373  ConstructLegalDocumentsText();
2374  OnWalletOrSigninUpdate();
2375}
2376
2377void AutofillDialogControllerImpl::OnDidSaveToWallet(
2378    const std::string& instrument_id,
2379    const std::string& address_id,
2380    const std::vector<wallet::RequiredAction>& required_actions,
2381    const std::vector<wallet::FormFieldError>& form_field_errors) {
2382  DCHECK(is_submitting_ && IsPayingWithWallet());
2383
2384  if (required_actions.empty()) {
2385    if (!address_id.empty())
2386      active_address_id_ = address_id;
2387    if (!instrument_id.empty())
2388      active_instrument_id_ = instrument_id;
2389    GetFullWallet();
2390  } else {
2391    OnWalletFormFieldError(form_field_errors);
2392    HandleSaveOrUpdateRequiredActions(required_actions);
2393  }
2394}
2395
2396void AutofillDialogControllerImpl::OnWalletError(
2397    wallet::WalletClient::ErrorType error_type) {
2398  DisableWallet(error_type);
2399}
2400
2401////////////////////////////////////////////////////////////////////////////////
2402// PersonalDataManagerObserver implementation.
2403
2404void AutofillDialogControllerImpl::OnPersonalDataChanged() {
2405  if (is_submitting_)
2406    return;
2407
2408  SuggestionsUpdated();
2409}
2410
2411////////////////////////////////////////////////////////////////////////////////
2412// AccountChooserModelDelegate implementation.
2413
2414void AutofillDialogControllerImpl::AccountChooserWillShow() {
2415  HidePopup();
2416}
2417
2418void AutofillDialogControllerImpl::AccountChoiceChanged() {
2419  ScopedViewUpdates updates(view_.get());
2420  wallet::WalletClient* client = GetWalletClient();
2421
2422  if (is_submitting_)
2423    client->CancelRequests();
2424
2425  SetIsSubmitting(false);
2426
2427  size_t selected_user_index =
2428      account_chooser_model_.GetActiveWalletAccountIndex();
2429  if (account_chooser_model_.WalletIsSelected() &&
2430      client->user_index() != selected_user_index) {
2431    client->set_user_index(selected_user_index);
2432    // Clear |wallet_items_| so we don't try to restore the selected instrument
2433    // and address.
2434    wallet_items_.reset();
2435    GetWalletItems();
2436  } else {
2437    SuggestionsUpdated();
2438    UpdateAccountChooserView();
2439  }
2440}
2441
2442void AutofillDialogControllerImpl::AddAccount() {
2443  SignInLinkClicked();
2444}
2445
2446void AutofillDialogControllerImpl::UpdateAccountChooserView() {
2447  if (view_) {
2448    ScopedViewUpdates updates(view_.get());
2449    view_->UpdateAccountChooser();
2450    view_->UpdateNotificationArea();
2451  }
2452}
2453
2454////////////////////////////////////////////////////////////////////////////////
2455
2456bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
2457    const content::NativeWebKeyboardEvent& event) {
2458  if (popup_controller_.get())
2459    return popup_controller_->HandleKeyPressEvent(event);
2460
2461  return false;
2462}
2463
2464bool AutofillDialogControllerImpl::RequestingCreditCardInfo() const {
2465  DCHECK_GT(form_structure_.field_count(), 0U);
2466
2467  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
2468    AutofillType type = form_structure_.field(i)->Type();
2469    if (common::IsCreditCardType(type.GetStorableType()))
2470      return true;
2471  }
2472
2473  return false;
2474}
2475
2476bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
2477  return source_url_.SchemeIs(content::kHttpsScheme);
2478}
2479
2480bool AutofillDialogControllerImpl::IsSubmitPausedOn(
2481    wallet::RequiredAction required_action) const {
2482  return full_wallet_ && full_wallet_->HasRequiredAction(required_action);
2483}
2484
2485void AutofillDialogControllerImpl::ShowNewCreditCardBubble(
2486    scoped_ptr<CreditCard> new_card,
2487    scoped_ptr<AutofillProfile> billing_profile) {
2488#if !defined(OS_ANDROID)
2489  NewCreditCardBubbleController::Show(web_contents(),
2490                                      new_card.Pass(),
2491                                      billing_profile.Pass());
2492#endif
2493}
2494
2495void AutofillDialogControllerImpl::SubmitButtonDelayBegin() {
2496  submit_button_delay_timer_.Start(
2497      FROM_HERE,
2498      base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs),
2499      this,
2500      &AutofillDialogControllerImpl::OnSubmitButtonDelayEnd);
2501}
2502
2503void AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting() {
2504  DCHECK(submit_button_delay_timer_.IsRunning());
2505  submit_button_delay_timer_.user_task().Run();
2506  submit_button_delay_timer_.Stop();
2507}
2508
2509void AutofillDialogControllerImpl::
2510    ClearLastWalletItemsFetchTimestampForTesting() {
2511  last_wallet_items_fetch_timestamp_ = base::TimeTicks();
2512}
2513
2514AccountChooserModel* AutofillDialogControllerImpl::
2515    AccountChooserModelForTesting() {
2516  return &account_chooser_model_;
2517}
2518
2519bool AutofillDialogControllerImpl::IsSignInContinueUrl(
2520    const GURL& url,
2521    size_t* user_index) const {
2522  return wallet::IsSignInContinueUrl(url, user_index);
2523}
2524
2525AutofillDialogControllerImpl::AutofillDialogControllerImpl(
2526    content::WebContents* contents,
2527    const FormData& form_structure,
2528    const GURL& source_url,
2529    const base::Callback<void(const FormStructure*)>& callback)
2530    : WebContentsObserver(contents),
2531      profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
2532      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
2533      form_structure_(form_structure),
2534      invoked_from_same_origin_(true),
2535      source_url_(source_url),
2536      callback_(callback),
2537      account_chooser_model_(this, profile_, metric_logger_),
2538      wallet_client_(profile_->GetRequestContext(), this, source_url),
2539      wallet_items_requested_(false),
2540      handling_use_wallet_link_click_(false),
2541      passive_failed_(false),
2542      country_combobox_model_(*GetManager()),
2543      suggested_cc_(this),
2544      suggested_billing_(this),
2545      suggested_cc_billing_(this),
2546      suggested_shipping_(this),
2547      cares_about_shipping_(true),
2548      input_showing_popup_(NULL),
2549      weak_ptr_factory_(this),
2550      waiting_for_explicit_sign_in_response_(false),
2551      has_accepted_legal_documents_(false),
2552      is_submitting_(false),
2553      choose_another_instrument_or_address_(false),
2554      wallet_server_validation_recoverable_(true),
2555      data_was_passed_back_(false),
2556      was_ui_latency_logged_(false),
2557      card_generated_animation_(2000, 60, this) {
2558  // TODO(estade): remove duplicates from |form_structure|?
2559  DCHECK(!callback_.is_null());
2560}
2561
2562AutofillDialogView* AutofillDialogControllerImpl::CreateView() {
2563  return AutofillDialogView::Create(this);
2564}
2565
2566PersonalDataManager* AutofillDialogControllerImpl::GetManager() {
2567  return PersonalDataManagerFactory::GetForProfile(profile_);
2568}
2569
2570const wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient()
2571    const {
2572  return const_cast<AutofillDialogControllerImpl*>(this)->GetWalletClient();
2573}
2574
2575wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient() {
2576  return &wallet_client_;
2577}
2578
2579bool AutofillDialogControllerImpl::IsPayingWithWallet() const {
2580  return account_chooser_model_.WalletIsSelected() &&
2581         SignedInState() == SIGNED_IN;
2582}
2583
2584void AutofillDialogControllerImpl::LoadRiskFingerprintData() {
2585  risk_data_.clear();
2586
2587  uint64 obfuscated_gaia_id = 0;
2588  bool success = base::StringToUint64(wallet_items_->obfuscated_gaia_id(),
2589                                      &obfuscated_gaia_id);
2590  DCHECK(success);
2591
2592  gfx::Rect window_bounds;
2593  window_bounds = GetBaseWindowForWebContents(web_contents())->GetBounds();
2594
2595  PrefService* user_prefs = profile_->GetPrefs();
2596  std::string charset = user_prefs->GetString(::prefs::kDefaultCharset);
2597  std::string accept_languages =
2598      user_prefs->GetString(::prefs::kAcceptLanguages);
2599  base::Time install_time = base::Time::FromTimeT(
2600      g_browser_process->local_state()->GetInt64(::prefs::kInstallDate));
2601
2602  risk::GetFingerprint(
2603      obfuscated_gaia_id, window_bounds, *web_contents(),
2604      chrome::VersionInfo().Version(), charset, accept_languages, install_time,
2605      g_browser_process->GetApplicationLocale(),
2606      base::Bind(&AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData,
2607                 weak_ptr_factory_.GetWeakPtr()));
2608}
2609
2610void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
2611    scoped_ptr<risk::Fingerprint> fingerprint) {
2612  DCHECK(AreLegalDocumentsCurrent());
2613
2614  std::string proto_data;
2615  fingerprint->SerializeToString(&proto_data);
2616  bool success = base::Base64Encode(proto_data, &risk_data_);
2617  DCHECK(success);
2618
2619  SubmitWithWallet();
2620}
2621
2622void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL& url) {
2623  chrome::NavigateParams params(
2624      chrome::FindBrowserWithWebContents(web_contents()),
2625      url,
2626      content::PAGE_TRANSITION_LINK);
2627  params.disposition = NEW_FOREGROUND_TAB;
2628  chrome::Navigate(&params);
2629}
2630
2631bool AutofillDialogControllerImpl::IsEditingExistingData(
2632    DialogSection section) const {
2633  return section_editing_state_.count(section) > 0;
2634}
2635
2636bool AutofillDialogControllerImpl::IsManuallyEditingSection(
2637    DialogSection section) const {
2638  return IsEditingExistingData(section) ||
2639         SuggestionsMenuModelForSection(section)->
2640             GetItemKeyForCheckedItem() == kAddNewItemKey;
2641}
2642
2643void AutofillDialogControllerImpl::OnWalletSigninError() {
2644  account_chooser_model_.SetHadWalletSigninError();
2645  GetWalletClient()->CancelRequests();
2646  LogDialogLatencyToShow();
2647}
2648
2649void AutofillDialogControllerImpl::DisableWallet(
2650    wallet::WalletClient::ErrorType error_type) {
2651  signin_helper_.reset();
2652  wallet_items_.reset();
2653  wallet_errors_.clear();
2654  GetWalletClient()->CancelRequests();
2655  SetIsSubmitting(false);
2656  wallet_error_notification_ = GetWalletError(error_type);
2657  account_chooser_model_.SetHadWalletError();
2658}
2659
2660void AutofillDialogControllerImpl::SuggestionsUpdated() {
2661  ScopedViewUpdates updates(view_.get());
2662
2663  const FieldValueMap snapshot = TakeUserInputSnapshot();
2664
2665  suggested_cc_.Reset();
2666  suggested_billing_.Reset();
2667  suggested_cc_billing_.Reset();
2668  suggested_shipping_.Reset();
2669  HidePopup();
2670
2671  suggested_shipping_.AddKeyedItem(
2672      kSameAsBillingKey,
2673      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING));
2674
2675  if (IsPayingWithWallet()) {
2676    const std::vector<wallet::Address*>& addresses =
2677        wallet_items_->addresses();
2678    for (size_t i = 0; i < addresses.size(); ++i) {
2679      std::string key = base::IntToString(i);
2680      suggested_shipping_.AddKeyedItemWithMinorText(
2681          key,
2682          addresses[i]->DisplayName(),
2683          addresses[i]->DisplayNameDetail());
2684
2685      const std::string default_shipping_address_id =
2686          GetIdToSelect(wallet_items_->default_address_id(),
2687                        previous_default_shipping_address_id_,
2688                        previously_selected_shipping_address_id_);
2689      if (addresses[i]->object_id() == default_shipping_address_id)
2690        suggested_shipping_.SetCheckedItem(key);
2691    }
2692
2693    if (!IsSubmitPausedOn(wallet::VERIFY_CVV)) {
2694      const std::vector<wallet::WalletItems::MaskedInstrument*>& instruments =
2695          wallet_items_->instruments();
2696      std::string first_active_instrument_key;
2697      std::string default_instrument_key;
2698      for (size_t i = 0; i < instruments.size(); ++i) {
2699        bool allowed = IsInstrumentAllowed(*instruments[i]);
2700        gfx::Image icon = instruments[i]->CardIcon();
2701        if (!allowed && !icon.IsEmpty()) {
2702          // Create a grayed disabled icon.
2703          SkBitmap disabled_bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
2704              *icon.ToSkBitmap(), kGrayImageShift);
2705          icon = gfx::Image(
2706              gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap));
2707        }
2708        std::string key = base::IntToString(i);
2709        suggested_cc_billing_.AddKeyedItemWithMinorTextAndIcon(
2710            key,
2711            instruments[i]->DisplayName(),
2712            instruments[i]->DisplayNameDetail(),
2713            icon);
2714        suggested_cc_billing_.SetEnabled(key, allowed);
2715
2716        if (allowed) {
2717          if (first_active_instrument_key.empty())
2718            first_active_instrument_key = key;
2719
2720          const std::string default_instrument_id =
2721              GetIdToSelect(wallet_items_->default_instrument_id(),
2722                            previous_default_instrument_id_,
2723                            previously_selected_instrument_id_);
2724          if (instruments[i]->object_id() == default_instrument_id)
2725            default_instrument_key = key;
2726        }
2727      }
2728
2729      suggested_cc_billing_.AddKeyedItem(
2730          kAddNewItemKey,
2731          l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_DETAILS));
2732      if (!wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2733        suggested_cc_billing_.AddKeyedItemWithMinorText(
2734            kManageItemsKey,
2735            l10n_util::GetStringUTF16(
2736                IDS_AUTOFILL_DIALOG_MANAGE_BILLING_DETAILS),
2737                UTF8ToUTF16(wallet::GetManageInstrumentsUrl(0U).host()));
2738      }
2739
2740      // Determine which instrument item should be selected.
2741      if (!default_instrument_key.empty())
2742        suggested_cc_billing_.SetCheckedItem(default_instrument_key);
2743      else if (!first_active_instrument_key.empty())
2744        suggested_cc_billing_.SetCheckedItem(first_active_instrument_key);
2745      else
2746        suggested_cc_billing_.SetCheckedItem(kAddNewItemKey);
2747    }
2748  } else {
2749    PersonalDataManager* manager = GetManager();
2750    const std::vector<CreditCard*>& cards = manager->GetCreditCards();
2751    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
2752    for (size_t i = 0; i < cards.size(); ++i) {
2753      if (!HasCompleteAndVerifiedData(*cards[i], requested_cc_fields_))
2754        continue;
2755
2756      suggested_cc_.AddKeyedItemWithIcon(
2757          cards[i]->guid(),
2758          cards[i]->Label(),
2759          rb.GetImageNamed(CreditCard::IconResourceId(cards[i]->type())));
2760    }
2761
2762    const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
2763    std::vector<base::string16> labels;
2764    AutofillProfile::CreateDifferentiatingLabels(profiles, &labels);
2765    DCHECK_EQ(labels.size(), profiles.size());
2766    const std::string app_locale = g_browser_process->GetApplicationLocale();
2767    for (size_t i = 0; i < profiles.size(); ++i) {
2768      const AutofillProfile& profile = *profiles[i];
2769      if (!HasCompleteAndVerifiedData(profile, requested_shipping_fields_) ||
2770          HasInvalidAddress(*profiles[i])) {
2771        continue;
2772      }
2773
2774      // Don't add variants for addresses: name is part of credit card and we'll
2775      // just ignore email and phone number variants.
2776      suggested_shipping_.AddKeyedItem(profile.guid(), labels[i]);
2777      if (!profile.GetRawInfo(EMAIL_ADDRESS).empty() &&
2778          !profile.IsPresentButInvalid(EMAIL_ADDRESS)) {
2779        suggested_billing_.AddKeyedItem(profile.guid(), labels[i]);
2780      }
2781    }
2782
2783    suggested_cc_.AddKeyedItem(
2784        kAddNewItemKey,
2785        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD));
2786    suggested_cc_.AddKeyedItem(
2787        kManageItemsKey,
2788        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD));
2789    suggested_billing_.AddKeyedItem(
2790        kAddNewItemKey,
2791        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS));
2792    suggested_billing_.AddKeyedItem(
2793        kManageItemsKey,
2794        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS));
2795  }
2796
2797  suggested_shipping_.AddKeyedItem(
2798      kAddNewItemKey,
2799      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS));
2800  if (!IsPayingWithWallet()) {
2801    suggested_shipping_.AddKeyedItem(
2802        kManageItemsKey,
2803        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS));
2804  } else if (!wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)) {
2805    suggested_shipping_.AddKeyedItemWithMinorText(
2806        kManageItemsKey,
2807        l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS),
2808        UTF8ToUTF16(wallet::GetManageAddressesUrl(0U).host()));
2809  }
2810
2811  if (!IsPayingWithWallet()) {
2812    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2813      DialogSection section = static_cast<DialogSection>(i);
2814      if (!SectionIsActive(section))
2815        continue;
2816
2817      // Set the starting choice for the menu. First set to the default in case
2818      // the GUID saved in prefs refers to a profile that no longer exists.
2819      std::string guid;
2820      GetDefaultAutofillChoice(section, &guid);
2821      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2822      model->SetCheckedItem(guid);
2823      if (GetAutofillChoice(section, &guid))
2824        model->SetCheckedItem(guid);
2825    }
2826  }
2827
2828  if (view_)
2829    view_->ModelChanged();
2830
2831  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2832    ResetSectionInput(static_cast<DialogSection>(i));
2833  }
2834
2835  RestoreUserInputFromSnapshot(snapshot);
2836
2837  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2838    DialogSection section = static_cast<DialogSection>(i);
2839    if (!SectionIsActive(section))
2840      continue;
2841
2842    ShowEditUiIfBadSuggestion(section);
2843    UpdateSection(section);
2844  }
2845
2846  UpdateForErrors();
2847}
2848
2849void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
2850    DialogSection section,
2851    const InputFieldComparator& compare) {
2852  const DetailInputs& inputs = RequestedFieldsForSection(section);
2853
2854  if (!SectionIsActive(section))
2855    return;
2856
2857  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2858  if (wrapper) {
2859    // Only fill in data that is associated with this section.
2860    const DetailInputs& inputs = RequestedFieldsForSection(section);
2861    wrapper->FillFormStructure(inputs, compare, &form_structure_);
2862
2863    // CVC needs special-casing because the CreditCard class doesn't store or
2864    // handle them. This isn't necessary when filling the combined CC and
2865    // billing section as CVC comes from |full_wallet_| in this case.
2866    if (section == SECTION_CC)
2867      SetOutputForFieldsOfType(CREDIT_CARD_VERIFICATION_CODE, view_->GetCvc());
2868
2869    // When filling from Wallet data, use the email address associated with the
2870    // account. There is no other email address stored as part of a Wallet
2871    // address.
2872    if (section == SECTION_CC_BILLING) {
2873      SetOutputForFieldsOfType(
2874          EMAIL_ADDRESS, account_chooser_model_.GetActiveWalletAccountName());
2875    }
2876  } else {
2877    // The user manually input data. If using Autofill, save the info as new or
2878    // edited data. Always fill local data into |form_structure_|.
2879    FieldValueMap output;
2880    view_->GetUserInput(section, &output);
2881
2882    if (section == SECTION_CC) {
2883      CreditCard card;
2884      card.set_origin(kAutofillDialogOrigin);
2885      FillFormGroupFromOutputs(output, &card);
2886
2887      // The card holder name comes from the billing address section.
2888      card.SetRawInfo(CREDIT_CARD_NAME,
2889                      GetValueFromSection(SECTION_BILLING, NAME_BILLING_FULL));
2890
2891      if (ShouldSaveDetailsLocally()) {
2892        std::string guid = GetManager()->SaveImportedCreditCard(card);
2893        newly_saved_data_model_guids_[section] = guid;
2894        DCHECK(!profile()->IsOffTheRecord());
2895        newly_saved_card_.reset(new CreditCard(card));
2896      }
2897
2898      AutofillCreditCardWrapper card_wrapper(&card);
2899      card_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2900
2901      // Again, CVC needs special-casing. Fill it in directly from |output|.
2902      SetOutputForFieldsOfType(
2903          CREDIT_CARD_VERIFICATION_CODE,
2904          output[CREDIT_CARD_VERIFICATION_CODE]);
2905    } else {
2906      AutofillProfile profile;
2907      profile.set_origin(kAutofillDialogOrigin);
2908      FillFormGroupFromOutputs(output, &profile);
2909
2910      if (ShouldSaveDetailsLocally()) {
2911        std::string guid = GetManager()->SaveImportedProfile(profile);
2912        newly_saved_data_model_guids_[section] = guid;
2913      }
2914
2915      AutofillProfileWrapper profile_wrapper(&profile);
2916      profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
2917    }
2918  }
2919}
2920
2921void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) {
2922  FillOutputForSectionWithComparator(
2923      section, base::Bind(common::DetailInputMatchesField, section));
2924}
2925
2926bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
2927    DialogSection section) const {
2928  // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
2929  // any of the fields.
2930  if (section == SECTION_SHIPPING)
2931    return cares_about_shipping_;
2932
2933  return true;
2934}
2935
2936void AutofillDialogControllerImpl::SetOutputForFieldsOfType(
2937    ServerFieldType type,
2938    const base::string16& output) {
2939  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
2940    AutofillField* field = form_structure_.field(i);
2941    if (field->Type().GetStorableType() == type)
2942      field->value = output;
2943  }
2944}
2945
2946string16 AutofillDialogControllerImpl::GetValueFromSection(
2947    DialogSection section,
2948    ServerFieldType type) {
2949  DCHECK(SectionIsActive(section));
2950
2951  scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
2952  if (wrapper)
2953    return wrapper->GetInfo(AutofillType(type));
2954
2955  FieldValueMap output;
2956  view_->GetUserInput(section, &output);
2957  return output[type];
2958}
2959
2960SuggestionsMenuModel* AutofillDialogControllerImpl::
2961    SuggestionsMenuModelForSection(DialogSection section) {
2962  switch (section) {
2963    case SECTION_CC:
2964      return &suggested_cc_;
2965    case SECTION_BILLING:
2966      return &suggested_billing_;
2967    case SECTION_SHIPPING:
2968      return &suggested_shipping_;
2969    case SECTION_CC_BILLING:
2970      return &suggested_cc_billing_;
2971  }
2972
2973  NOTREACHED();
2974  return NULL;
2975}
2976
2977const SuggestionsMenuModel* AutofillDialogControllerImpl::
2978    SuggestionsMenuModelForSection(DialogSection section) const {
2979  return const_cast<AutofillDialogControllerImpl*>(this)->
2980      SuggestionsMenuModelForSection(section);
2981}
2982
2983DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
2984    const SuggestionsMenuModel& model) {
2985  if (&model == &suggested_cc_)
2986    return SECTION_CC;
2987
2988  if (&model == &suggested_billing_)
2989    return SECTION_BILLING;
2990
2991  if (&model == &suggested_cc_billing_)
2992    return SECTION_CC_BILLING;
2993
2994  DCHECK_EQ(&model, &suggested_shipping_);
2995  return SECTION_SHIPPING;
2996}
2997
2998DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
2999    DialogSection section) {
3000  return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
3001}
3002
3003void AutofillDialogControllerImpl::HidePopup() {
3004  if (popup_controller_.get())
3005    popup_controller_->Hide();
3006  input_showing_popup_ = NULL;
3007}
3008
3009void AutofillDialogControllerImpl::SetEditingExistingData(
3010    DialogSection section, bool editing) {
3011  if (editing)
3012    section_editing_state_.insert(section);
3013  else
3014    section_editing_state_.erase(section);
3015}
3016
3017bool AutofillDialogControllerImpl::IsASuggestionItemKey(
3018    const std::string& key) const {
3019  return !key.empty() &&
3020      key != kAddNewItemKey &&
3021      key != kManageItemsKey &&
3022      key != kSameAsBillingKey;
3023}
3024
3025bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
3026  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
3027    if (IsManuallyEditingSection(static_cast<DialogSection>(section)))
3028      return true;
3029  }
3030  return false;
3031}
3032
3033base::string16 AutofillDialogControllerImpl::CreditCardNumberValidityMessage(
3034    const base::string16& number) const {
3035  if (!number.empty() && !autofill::IsValidCreditCardNumber(number)) {
3036    return l10n_util::GetStringUTF16(
3037        IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER);
3038  }
3039
3040  base::string16 message;
3041  if (IsPayingWithWallet() && !wallet_items_->SupportsCard(number, &message))
3042    return message;
3043
3044  // Card number is good and supported.
3045  return base::string16();
3046}
3047
3048bool AutofillDialogControllerImpl::AllSectionsAreValid() {
3049  for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
3050    if (!SectionIsValid(static_cast<DialogSection>(section)))
3051      return false;
3052  }
3053  return true;
3054}
3055
3056bool AutofillDialogControllerImpl::SectionIsValid(
3057    DialogSection section) {
3058  if (!IsManuallyEditingSection(section))
3059    return true;
3060
3061  FieldValueMap detail_outputs;
3062  view_->GetUserInput(section, &detail_outputs);
3063  return !InputsAreValid(section, detail_outputs).HasSureErrors();
3064}
3065
3066bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
3067    const base::string16& year,
3068    const base::string16& month) const {
3069  // If the expiration is in the past as per the local clock, it's invalid.
3070  base::Time now = base::Time::Now();
3071  if (!autofill::IsValidCreditCardExpirationDate(year, month, now))
3072    return false;
3073
3074  if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING)) {
3075    const wallet::WalletItems::MaskedInstrument* instrument =
3076        ActiveInstrument();
3077    const std::string& locale = g_browser_process->GetApplicationLocale();
3078    int month_int;
3079    if (base::StringToInt(month, &month_int) &&
3080        instrument->status() ==
3081            wallet::WalletItems::MaskedInstrument::EXPIRED &&
3082        year ==
3083            instrument->GetInfo(
3084                AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), locale) &&
3085        month_int == instrument->expiration_month()) {
3086      // Otherwise, if the user is editing an instrument that's deemed expired
3087      // by the Online Wallet server, mark it invalid on selection.
3088      return false;
3089    }
3090  }
3091
3092  return true;
3093}
3094
3095bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
3096  return SectionIsActive(SECTION_SHIPPING) &&
3097      suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey;
3098}
3099
3100bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
3101  // It's possible that the user checked [X] Save details locally before
3102  // switching payment methods, so only ask the view whether to save details
3103  // locally if that checkbox is showing (currently if not paying with wallet).
3104  // Also, if the user isn't editing any sections, there's no data to save
3105  // locally.
3106  return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
3107}
3108
3109void AutofillDialogControllerImpl::SetIsSubmitting(bool submitting) {
3110  is_submitting_ = submitting;
3111
3112  if (!submitting)
3113    full_wallet_.reset();
3114
3115  if (view_) {
3116    ScopedViewUpdates updates(view_.get());
3117    view_->UpdateButtonStrip();
3118    view_->UpdateOverlay();
3119    view_->UpdateNotificationArea();
3120  }
3121}
3122
3123bool AutofillDialogControllerImpl::AreLegalDocumentsCurrent() const {
3124  return has_accepted_legal_documents_ ||
3125      (wallet_items_ && wallet_items_->legal_documents().empty());
3126}
3127
3128void AutofillDialogControllerImpl::AcceptLegalTerms() {
3129  content::BrowserThread::PostTask(
3130      content::BrowserThread::IO, FROM_HERE,
3131      base::Bind(&UserDidOptIntoLocationServices));
3132  PrefService* local_state = g_browser_process->local_state();
3133  ListPrefUpdate accepted(
3134      local_state, ::prefs::kAutofillDialogWalletLocationAcceptance);
3135  accepted->AppendIfNotPresent(new base::StringValue(
3136      account_chooser_model_.GetActiveWalletAccountName()));
3137
3138  if (AreLegalDocumentsCurrent()) {
3139    LoadRiskFingerprintData();
3140  } else {
3141    GetWalletClient()->AcceptLegalDocuments(
3142        wallet_items_->legal_documents(),
3143        wallet_items_->google_transaction_id());
3144  }
3145}
3146
3147void AutofillDialogControllerImpl::SubmitWithWallet() {
3148  active_instrument_id_.clear();
3149  active_address_id_.clear();
3150  full_wallet_.reset();
3151
3152  const wallet::WalletItems::MaskedInstrument* active_instrument =
3153      ActiveInstrument();
3154  if (!IsManuallyEditingSection(SECTION_CC_BILLING)) {
3155    active_instrument_id_ = active_instrument->object_id();
3156    DCHECK(!active_instrument_id_.empty());
3157  }
3158
3159  const wallet::Address* active_address = ActiveShippingAddress();
3160  if (!IsManuallyEditingSection(SECTION_SHIPPING) &&
3161      !ShouldUseBillingForShipping() &&
3162      IsShippingAddressRequired()) {
3163    active_address_id_ = active_address->object_id();
3164    DCHECK(!active_address_id_.empty());
3165  }
3166
3167  scoped_ptr<wallet::Instrument> inputted_instrument =
3168      CreateTransientInstrument();
3169  if (inputted_instrument && IsEditingExistingData(SECTION_CC_BILLING)) {
3170    inputted_instrument->set_object_id(active_instrument->object_id());
3171    DCHECK(!inputted_instrument->object_id().empty());
3172  }
3173
3174  scoped_ptr<wallet::Address> inputted_address;
3175  if (active_address_id_.empty() && IsShippingAddressRequired()) {
3176    if (ShouldUseBillingForShipping()) {
3177      const wallet::Address& address = inputted_instrument ?
3178          *inputted_instrument->address() : active_instrument->address();
3179      // Try to find an exact matched shipping address and use it for shipping,
3180      // otherwise save it as a new shipping address. http://crbug.com/225442
3181      const wallet::Address* duplicated_address =
3182          FindDuplicateAddress(wallet_items_->addresses(), address);
3183      if (duplicated_address) {
3184        active_address_id_ = duplicated_address->object_id();
3185        DCHECK(!active_address_id_.empty());
3186      } else {
3187        inputted_address.reset(new wallet::Address(address));
3188        DCHECK(inputted_address->object_id().empty());
3189      }
3190    } else {
3191      inputted_address = CreateTransientAddress();
3192      if (IsEditingExistingData(SECTION_SHIPPING)) {
3193        inputted_address->set_object_id(active_address->object_id());
3194        DCHECK(!inputted_address->object_id().empty());
3195      }
3196    }
3197  }
3198
3199  // If there's neither an address nor instrument to save, |GetFullWallet()|
3200  // is called when the risk fingerprint is loaded.
3201  if (!active_instrument_id_.empty() &&
3202      (!active_address_id_.empty() || !IsShippingAddressRequired())) {
3203    GetFullWallet();
3204    return;
3205  }
3206
3207  GetWalletClient()->SaveToWallet(inputted_instrument.Pass(),
3208                                  inputted_address.Pass());
3209}
3210
3211scoped_ptr<wallet::Instrument> AutofillDialogControllerImpl::
3212    CreateTransientInstrument() {
3213  if (!active_instrument_id_.empty())
3214    return scoped_ptr<wallet::Instrument>();
3215
3216  FieldValueMap output;
3217  view_->GetUserInput(SECTION_CC_BILLING, &output);
3218
3219  CreditCard card;
3220  AutofillProfile profile;
3221  string16 cvc;
3222  GetBillingInfoFromOutputs(output, &card, &cvc, &profile);
3223
3224  return scoped_ptr<wallet::Instrument>(
3225      new wallet::Instrument(card, cvc, profile));
3226}
3227
3228scoped_ptr<wallet::Address>AutofillDialogControllerImpl::
3229    CreateTransientAddress() {
3230  // If not using billing for shipping, just scrape the view.
3231  FieldValueMap output;
3232  view_->GetUserInput(SECTION_SHIPPING, &output);
3233
3234  AutofillProfile profile;
3235  FillFormGroupFromOutputs(output, &profile);
3236
3237  return scoped_ptr<wallet::Address>(new wallet::Address(profile));
3238}
3239
3240void AutofillDialogControllerImpl::GetFullWallet() {
3241  DCHECK(is_submitting_);
3242  DCHECK(IsPayingWithWallet());
3243  DCHECK(wallet_items_);
3244  DCHECK(!active_instrument_id_.empty());
3245  DCHECK(!active_address_id_.empty() || !IsShippingAddressRequired());
3246
3247  std::vector<wallet::WalletClient::RiskCapability> capabilities;
3248  capabilities.push_back(wallet::WalletClient::VERIFY_CVC);
3249
3250  GetWalletClient()->GetFullWallet(wallet::WalletClient::FullWalletRequest(
3251      active_instrument_id_,
3252      active_address_id_,
3253      wallet_items_->google_transaction_id(),
3254      capabilities,
3255      wallet_items_->HasRequiredAction(wallet::SETUP_WALLET)));
3256}
3257
3258void AutofillDialogControllerImpl::HandleSaveOrUpdateRequiredActions(
3259    const std::vector<wallet::RequiredAction>& required_actions) {
3260  DCHECK(!required_actions.empty());
3261
3262  // TODO(ahutter): Investigate if we need to support more generic actions on
3263  // this call such as GAIA_AUTH. See crbug.com/243457.
3264  for (std::vector<wallet::RequiredAction>::const_iterator iter =
3265           required_actions.begin();
3266       iter != required_actions.end(); ++iter) {
3267    if (*iter != wallet::INVALID_FORM_FIELD) {
3268      // TODO(dbeam): handle this more gracefully.
3269      DisableWallet(wallet::WalletClient::UNKNOWN_ERROR);
3270    }
3271  }
3272  SetIsSubmitting(false);
3273}
3274
3275void AutofillDialogControllerImpl::FinishSubmit() {
3276  if (IsPayingWithWallet()) {
3277    ScopedViewUpdates updates(view_.get());
3278    view_->UpdateOverlay();
3279
3280    card_generated_animation_.Start();
3281    return;
3282  }
3283  DoFinishSubmit();
3284}
3285
3286void AutofillDialogControllerImpl::AnimationProgressed(
3287    const gfx::Animation* animation) {
3288  DCHECK_EQ(animation, &card_generated_animation_);
3289  PushOverlayUpdate();
3290}
3291
3292void AutofillDialogControllerImpl::AnimationEnded(
3293    const gfx::Animation* animation) {
3294  DCHECK_EQ(animation, &card_generated_animation_);
3295  DoFinishSubmit();
3296}
3297
3298void AutofillDialogControllerImpl::DoFinishSubmit() {
3299  FillOutputForSection(SECTION_CC);
3300  FillOutputForSection(SECTION_BILLING);
3301  FillOutputForSection(SECTION_CC_BILLING);
3302
3303  if (ShouldUseBillingForShipping()) {
3304    FillOutputForSectionWithComparator(
3305        SECTION_BILLING,
3306        base::Bind(DetailInputMatchesShippingField));
3307    FillOutputForSectionWithComparator(
3308        SECTION_CC,
3309        base::Bind(DetailInputMatchesShippingField));
3310    FillOutputForSectionWithComparator(
3311        SECTION_CC_BILLING,
3312        base::Bind(DetailInputMatchesShippingField));
3313  } else {
3314    FillOutputForSection(SECTION_SHIPPING);
3315  }
3316
3317  if (!IsPayingWithWallet()) {
3318    for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
3319      DialogSection section = static_cast<DialogSection>(i);
3320      if (!SectionIsActive(section))
3321        continue;
3322
3323      SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
3324      std::string item_key = model->GetItemKeyForCheckedItem();
3325      if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) {
3326        PersistAutofillChoice(section, item_key);
3327      } else if (item_key == kAddNewItemKey && ShouldSaveDetailsLocally()) {
3328        DCHECK(newly_saved_data_model_guids_.count(section));
3329        PersistAutofillChoice(section, newly_saved_data_model_guids_[section]);
3330      }
3331    }
3332
3333    profile_->GetPrefs()->SetBoolean(::prefs::kAutofillDialogSaveData,
3334                                     view_->SaveDetailsLocally());
3335  }
3336
3337  // On a successful submit, if the user manually selected "pay without wallet",
3338  // stop trying to pay with Wallet on future runs of the dialog. On the other
3339  // hand, if there was an error that prevented the user from having the choice
3340  // of using Wallet, leave the pref alone.
3341  if (!wallet_error_notification_ &&
3342      account_chooser_model_.HasAccountsToChoose()) {
3343    profile_->GetPrefs()->SetBoolean(
3344        ::prefs::kAutofillDialogPayWithoutWallet,
3345        !account_chooser_model_.WalletIsSelected());
3346  }
3347
3348  LogOnFinishSubmitMetrics();
3349
3350  // Callback should be called as late as possible.
3351  callback_.Run(&form_structure_);
3352  data_was_passed_back_ = true;
3353
3354  // This might delete us.
3355  Hide();
3356}
3357
3358void AutofillDialogControllerImpl::PersistAutofillChoice(
3359    DialogSection section,
3360    const std::string& guid) {
3361  DCHECK(!IsPayingWithWallet());
3362  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
3363  value->SetString(kGuidPrefKey, guid);
3364
3365  DictionaryPrefUpdate updater(profile()->GetPrefs(),
3366                               ::prefs::kAutofillDialogAutofillDefault);
3367  base::DictionaryValue* autofill_choice = updater.Get();
3368  autofill_choice->Set(SectionToPrefString(section), value.release());
3369}
3370
3371void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
3372    DialogSection section,
3373    std::string* guid) {
3374  DCHECK(!IsPayingWithWallet());
3375  // The default choice is the first thing in the menu that is a suggestion
3376  // item.
3377  SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
3378  for (int i = 0; i < model->GetItemCount(); ++i) {
3379    if (IsASuggestionItemKey(model->GetItemKeyAt(i))) {
3380      *guid = model->GetItemKeyAt(i);
3381      break;
3382    }
3383  }
3384}
3385
3386bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section,
3387                                                     std::string* guid) {
3388  DCHECK(!IsPayingWithWallet());
3389  const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary(
3390      ::prefs::kAutofillDialogAutofillDefault);
3391  if (!choices)
3392    return false;
3393
3394  const base::DictionaryValue* choice = NULL;
3395  if (!choices->GetDictionary(SectionToPrefString(section), &choice))
3396    return false;
3397
3398  choice->GetString(kGuidPrefKey, guid);
3399  return true;
3400}
3401
3402void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
3403  GetMetricLogger().LogDialogUiDuration(
3404      base::Time::Now() - dialog_shown_timestamp_,
3405      AutofillMetrics::DIALOG_ACCEPTED);
3406
3407  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
3408
3409  AutofillMetrics::DialogDismissalState dismissal_state;
3410  if (!IsManuallyEditingAnySection())
3411    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_DATA;
3412  else if (IsPayingWithWallet())
3413    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_WALLET;
3414  else if (ShouldSaveDetailsLocally())
3415    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL;
3416  else
3417    dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE;
3418
3419  GetMetricLogger().LogDialogDismissalState(dismissal_state);
3420}
3421
3422void AutofillDialogControllerImpl::LogOnCancelMetrics() {
3423  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
3424
3425  AutofillMetrics::DialogDismissalState dismissal_state;
3426  if (ShouldShowSignInWebView())
3427    dismissal_state = AutofillMetrics::DIALOG_CANCELED_DURING_SIGNIN;
3428  else if (!IsManuallyEditingAnySection())
3429    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS;
3430  else if (AllSectionsAreValid())
3431    dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS;
3432  else
3433    dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS;
3434
3435  GetMetricLogger().LogDialogDismissalState(dismissal_state);
3436
3437  GetMetricLogger().LogDialogUiDuration(
3438      base::Time::Now() - dialog_shown_timestamp_,
3439      AutofillMetrics::DIALOG_CANCELED);
3440}
3441
3442void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
3443    const SuggestionsMenuModel& model) {
3444  DialogSection section = SectionForSuggestionsMenuModel(model);
3445
3446  AutofillMetrics::DialogUiEvent dialog_ui_event;
3447  if (model.GetItemKeyForCheckedItem() == kAddNewItemKey) {
3448    // Selected to add a new item.
3449    dialog_ui_event = common::DialogSectionToUiItemAddedEvent(section);
3450  } else if (IsASuggestionItemKey(model.GetItemKeyForCheckedItem())) {
3451    // Selected an existing item.
3452    dialog_ui_event = common::DialogSectionToUiSelectionChangedEvent(section);
3453  } else {
3454    // TODO(estade): add logging for "Manage items" or "Use billing for
3455    // shipping"?
3456    return;
3457  }
3458
3459  GetMetricLogger().LogDialogUiEvent(dialog_ui_event);
3460}
3461
3462void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
3463  if (was_ui_latency_logged_)
3464    return;
3465
3466  GetMetricLogger().LogDialogLatencyToShow(
3467      base::Time::Now() - dialog_shown_timestamp_);
3468  was_ui_latency_logged_ = true;
3469}
3470
3471AutofillMetrics::DialogInitialUserStateMetric
3472    AutofillDialogControllerImpl::GetInitialUserState() const {
3473  // Consider a user to be an Autofill user if the user has any credit cards
3474  // or addresses saved. Check that the item count is greater than 2 because
3475  // an "empty" menu still has the "add new" menu item and "manage" menu item.
3476  const bool has_autofill_profiles =
3477      suggested_cc_.GetItemCount() > 2 ||
3478      suggested_billing_.GetItemCount() > 2;
3479
3480  if (SignedInState() != SIGNED_IN) {
3481    // Not signed in.
3482    return has_autofill_profiles ?
3483        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL :
3484        AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL;
3485  }
3486
3487  // Signed in.
3488  if (wallet_items_->instruments().empty()) {
3489    // No Wallet items.
3490    return has_autofill_profiles ?
3491        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL :
3492        AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL;
3493  }
3494
3495  // Has Wallet items.
3496  return has_autofill_profiles ?
3497      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL :
3498      AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL;
3499}
3500
3501void AutofillDialogControllerImpl::MaybeShowCreditCardBubble() {
3502  if (!data_was_passed_back_)
3503    return;
3504
3505  if (newly_saved_card_) {
3506    scoped_ptr<AutofillProfile> billing_profile;
3507    if (IsManuallyEditingSection(SECTION_BILLING)) {
3508      // Scrape the view as the user's entering or updating information.
3509      FieldValueMap outputs;
3510      view_->GetUserInput(SECTION_BILLING, &outputs);
3511      billing_profile.reset(new AutofillProfile);
3512      FillFormGroupFromOutputs(outputs, billing_profile.get());
3513    } else {
3514      // Just snag the currently suggested profile.
3515      std::string item_key = SuggestionsMenuModelForSection(SECTION_BILLING)->
3516          GetItemKeyForCheckedItem();
3517      AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
3518      billing_profile.reset(new AutofillProfile(*profile));
3519    }
3520
3521    ShowNewCreditCardBubble(newly_saved_card_.Pass(),
3522                            billing_profile.Pass());
3523    return;
3524  }
3525
3526  if (!full_wallet_ || !full_wallet_->billing_address())
3527    return;
3528
3529#if !defined(OS_ANDROID)
3530  GeneratedCreditCardBubbleController::Show(
3531      web_contents(),
3532      full_wallet_->TypeAndLastFourDigits(),
3533      backing_card_last_four_);
3534#endif
3535}
3536
3537void AutofillDialogControllerImpl::OnSubmitButtonDelayEnd() {
3538  if (!view_)
3539    return;
3540  ScopedViewUpdates updates(view_.get());
3541  view_->UpdateButtonStrip();
3542}
3543
3544void AutofillDialogControllerImpl::FetchWalletCookie() {
3545  net::URLRequestContextGetter* request_context = profile_->GetRequestContext();
3546  signin_helper_.reset(new wallet::WalletSigninHelper(this, request_context));
3547  signin_helper_->StartWalletCookieValueFetch();
3548}
3549
3550}  // namespace autofill
3551