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.
5 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
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"
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"
95 const char kAddNewItemKey
[] = "add-new-item";
96 const char kManageItemsKey
[] = "manage-items";
97 const char kSameAsBillingKey
[] = "same-as-billing";
99 // URLs for Wallet error messages.
100 const char kBuyerLegalAddressStatusUrl
[] =
101 "https://wallet.google.com/manage/settings";
102 const char kKnowYourCustomerStatusUrl
[] = "https://wallet.google.com/kyc";
104 // Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
106 const char kGuidPrefKey
[] = "guid";
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
111 const char kAutofillDialogOrigin
[] = "Chrome Autofill dialog";
113 // HSL shift to gray out an image.
114 const color_utils::HSL kGrayImageShift
= {-1, 0, 0.8};
116 // Limit Wallet items refresh rate to at most once per minute.
117 const int64 kWalletItemsRefreshRateSeconds
= 60;
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.
122 const int kSubmitButtonDelayMs
= 1000;
124 // A helper class to make sure an AutofillDialogView knows when a series of
125 // updates is incoming.
126 class ScopedViewUpdates
{
128 explicit ScopedViewUpdates(AutofillDialogView
* view
) : view_(view
) {
130 view_
->UpdatesStarted();
133 ~ScopedViewUpdates() {
135 view_
->UpdatesFinished();
139 AutofillDialogView
* view_
;
141 DISALLOW_COPY_AND_ASSIGN(ScopedViewUpdates
);
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.
147 bool DetailInputMatchesShippingField(const DetailInput
& input
,
148 const AutofillField
& field
) {
149 // Equivalent billing field type is used to support UseBillingAsShipping
151 ServerFieldType field_type
=
152 AutofillType::GetEquivalentBillingFieldType(
153 field
.Type().GetStorableType());
155 return common::InputTypeMatchesFieldType(input
, AutofillType(field_type
));
158 // Initializes |form_group| from user-entered data.
159 void FillFormGroupFromOutputs(const DetailOutputMap
& detail_outputs
,
160 FormGroup
* form_group
) {
161 for (DetailOutputMap::const_iterator iter
= detail_outputs
.begin();
162 iter
!= detail_outputs
.end(); ++iter
) {
163 ServerFieldType type
= iter
->first
->type
;
164 if (!iter
->second
.empty()) {
165 if (type
== ADDRESS_HOME_COUNTRY
|| type
== ADDRESS_BILLING_COUNTRY
) {
166 form_group
->SetInfo(AutofillType(type
),
168 g_browser_process
->GetApplicationLocale());
170 form_group
->SetRawInfo(
171 AutofillType(type
).GetStorableType(), iter
->second
);
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.
180 void GetBillingInfoFromOutputs(const DetailOutputMap
& output
,
183 AutofillProfile
* profile
) {
184 for (DetailOutputMap::const_iterator it
= output
.begin();
185 it
!= output
.end(); ++it
) {
187 TrimWhitespace(it
->second
, TRIM_ALL
, &trimmed
);
189 // Special case CVC as CreditCard just swallows it.
190 if (it
->first
->type
== CREDIT_CARD_VERIFICATION_CODE
) {
192 cvc
->assign(trimmed
);
193 } else if (it
->first
->type
== ADDRESS_HOME_COUNTRY
||
194 it
->first
->type
== ADDRESS_BILLING_COUNTRY
) {
196 profile
->SetInfo(AutofillType(it
->first
->type
),
198 g_browser_process
->GetApplicationLocale());
201 // Copy the credit card name to |profile| in addition to |card| as
202 // wallet::Instrument requires a recipient name for its billing address.
203 if (card
&& it
->first
->type
== NAME_FULL
)
204 card
->SetRawInfo(CREDIT_CARD_NAME
, trimmed
);
206 if (common::IsCreditCardType(it
->first
->type
)) {
208 card
->SetRawInfo(it
->first
->type
, trimmed
);
209 } else if (profile
) {
211 AutofillType(it
->first
->type
).GetStorableType(), trimmed
);
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.
220 ui::BaseWindow
* GetBaseWindowForWebContents(
221 const content::WebContents
* web_contents
) {
222 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
224 return browser
->window();
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();
234 // Extracts the string value of a field with |type| from |output|. This is
235 // useful when you only need the value of 1 input from a section of view inputs.
236 string16
GetValueForType(const DetailOutputMap
& output
,
237 ServerFieldType type
) {
238 for (DetailOutputMap::const_iterator it
= output
.begin();
239 it
!= output
.end(); ++it
) {
240 if (it
->first
->type
== type
)
246 // Returns a string descriptor for a DialogSection, for use with prefs (do not
247 // change these values).
248 std::string
SectionToPrefString(DialogSection section
) {
253 case SECTION_BILLING
:
256 case SECTION_CC_BILLING
:
257 // The SECTION_CC_BILLING section isn't active when using Autofill.
259 return std::string();
261 case SECTION_SHIPPING
:
266 return std::string();
269 // Check if a given MaskedInstrument is allowed for the purchase.
270 bool IsInstrumentAllowed(
271 const wallet::WalletItems::MaskedInstrument
& instrument
) {
272 switch (instrument
.status()) {
273 case wallet::WalletItems::MaskedInstrument::VALID
:
274 case wallet::WalletItems::MaskedInstrument::PENDING
:
275 case wallet::WalletItems::MaskedInstrument::EXPIRED
:
276 case wallet::WalletItems::MaskedInstrument::BILLING_INCOMPLETE
:
283 // Signals that the user has opted in to geolocation services. Factored out
284 // into a separate method because all interaction with the geolocation provider
285 // needs to happen on the IO thread, which is not the thread
286 // AutofillDialogViewDelegate lives on.
287 void UserDidOptIntoLocationServices() {
288 content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
291 // Returns whether |data_model| is complete, i.e. can fill out all the
292 // |requested_fields|, and verified, i.e. not just automatically aggregated.
293 // Incomplete or unverifed data will not be displayed in the dropdown menu.
294 bool HasCompleteAndVerifiedData(const AutofillDataModel
& data_model
,
295 const DetailInputs
& requested_fields
) {
296 if (!data_model
.IsVerified())
299 for (size_t i
= 0; i
< requested_fields
.size(); ++i
) {
300 ServerFieldType type
=
301 AutofillType(requested_fields
[i
].type
).GetStorableType();
302 if (type
!= ADDRESS_HOME_LINE2
&&
303 type
!= CREDIT_CARD_VERIFICATION_CODE
&&
304 data_model
.GetRawInfo(type
).empty()) {
312 // Returns true if |profile| has an invalid address, i.e. an invalid state, zip
313 // code, phone number, or email address. Otherwise returns false. Profiles with
314 // invalid addresses are not suggested in the dropdown menu for billing and
315 // shipping addresses.
316 bool HasInvalidAddress(const AutofillProfile
& profile
) {
317 return profile
.IsPresentButInvalid(ADDRESS_HOME_STATE
) ||
318 profile
.IsPresentButInvalid(ADDRESS_HOME_ZIP
) ||
319 profile
.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER
);
322 // Loops through |addresses_| comparing to |address| ignoring ID. If a match
323 // is not found, NULL is returned.
324 const wallet::Address
* FindDuplicateAddress(
325 const std::vector
<wallet::Address
*>& addresses
,
326 const wallet::Address
& address
) {
327 for (size_t i
= 0; i
< addresses
.size(); ++i
) {
328 if (addresses
[i
]->EqualsIgnoreID(address
))
334 bool IsCardHolderNameValidForWallet(const string16
& name
) {
335 base::string16 whitespace_collapsed_name
= CollapseWhitespace(name
, true);
336 std::vector
<base::string16
> split_name
;
337 base::SplitString(whitespace_collapsed_name
, ' ', &split_name
);
338 return split_name
.size() >= 2;
341 DialogSection
SectionFromLocation(wallet::FormFieldError::Location location
) {
343 case wallet::FormFieldError::PAYMENT_INSTRUMENT
:
344 case wallet::FormFieldError::LEGAL_ADDRESS
:
345 return SECTION_CC_BILLING
;
347 case wallet::FormFieldError::SHIPPING_ADDRESS
:
348 return SECTION_SHIPPING
;
350 case wallet::FormFieldError::UNKNOWN_LOCATION
:
359 scoped_ptr
<DialogNotification
> GetWalletError(
360 wallet::WalletClient::ErrorType error_type
) {
364 switch (error_type
) {
365 case wallet::WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS
:
366 text
= l10n_util::GetStringUTF16(
367 IDS_AUTOFILL_WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS
);
368 url
= GURL(kKnowYourCustomerStatusUrl
);
371 case wallet::WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED
:
372 text
= l10n_util::GetStringUTF16(
373 IDS_AUTOFILL_WALLET_BUYER_COUNTRY_NOT_SUPPORTED
);
374 url
= GURL(kBuyerLegalAddressStatusUrl
);
378 // The notification will not have a link; it's handled in the next
384 scoped_ptr
<DialogNotification
> notification(new DialogNotification(
385 DialogNotification::WALLET_ERROR
,
387 notification
->set_link_url(url
);
388 return notification
.Pass();
394 switch (error_type
) {
395 case wallet::WalletClient::UNSUPPORTED_MERCHANT
:
396 error_ids
= IDS_AUTOFILL_WALLET_UNSUPPORTED_MERCHANT
;
399 case wallet::WalletClient::BAD_REQUEST
:
400 error_ids
= IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR
;
404 case wallet::WalletClient::INVALID_PARAMS
:
405 error_ids
= IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR
;
409 case wallet::WalletClient::BUYER_ACCOUNT_ERROR
:
410 error_ids
= IDS_AUTOFILL_WALLET_BUYER_ACCOUNT_ERROR
;
414 case wallet::WalletClient::UNSUPPORTED_API_VERSION
:
415 error_ids
= IDS_AUTOFILL_WALLET_UPGRADE_CHROME_ERROR
;
419 case wallet::WalletClient::SERVICE_UNAVAILABLE
:
420 error_ids
= IDS_AUTOFILL_WALLET_SERVICE_UNAVAILABLE_ERROR
;
424 case wallet::WalletClient::INTERNAL_ERROR
:
425 error_ids
= IDS_AUTOFILL_WALLET_UNKNOWN_ERROR
;
429 case wallet::WalletClient::MALFORMED_RESPONSE
:
430 error_ids
= IDS_AUTOFILL_WALLET_UNKNOWN_ERROR
;
434 case wallet::WalletClient::NETWORK_ERROR
:
435 error_ids
= IDS_AUTOFILL_WALLET_UNKNOWN_ERROR
;
439 case wallet::WalletClient::UNKNOWN_ERROR
:
440 error_ids
= IDS_AUTOFILL_WALLET_UNKNOWN_ERROR
;
448 DCHECK_NE(0, error_ids
);
450 // The other error types are strings of the form "XXX. You can pay without
452 scoped_ptr
<DialogNotification
> notification(new DialogNotification(
453 DialogNotification::WALLET_ERROR
,
454 l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_COMPLETE_WITHOUT_WALLET
,
455 l10n_util::GetStringUTF16(error_ids
))));
458 notification
->set_tooltip_text(
459 l10n_util::GetStringFUTF16(IDS_AUTOFILL_WALLET_ERROR_CODE_TOOLTIP
,
460 base::IntToString16(error_code
)));
463 return notification
.Pass();
466 // Returns the ID of the address or instrument that should be selected in the
467 // UI, given that the |default_id| is currently the default ID on the Wallet
468 // server, |previous_default_id| was the default ID prior to re-fetching the
469 // Wallet data, and |previously_selected_id| was the ID of the item selected in
470 // the dialog prior to re-fetching the Wallet data.
471 std::string
GetIdToSelect(const std::string
& default_id
,
472 const std::string
& previous_default_id
,
473 const std::string
& previously_selected_id
) {
474 // If the default ID changed since the last fetch of the Wallet data, select
475 // it rather than the previously selected item, as the user's intention in
476 // changing the default was probably to use it.
477 if (default_id
!= previous_default_id
)
480 // Otherwise, prefer the previously selected item, if there was one.
481 return !previously_selected_id
.empty() ? previously_selected_id
: default_id
;
484 // Generate a random card number in a user displayable format.
485 base::string16
GenerateRandomCardNumber() {
486 std::string card_number
;
487 for (size_t i
= 0; i
< 4; ++i
) {
488 int part
= base::RandInt(0, 10000);
489 base::StringAppendF(&card_number
, "%04d ", part
);
491 return ASCIIToUTF16(card_number
);
494 gfx::Image
CreditCardIconForType(const std::string
& credit_card_type
) {
495 const int input_card_idr
= CreditCard::IconResourceId(credit_card_type
);
496 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
497 gfx::Image result
= rb
.GetImageNamed(input_card_idr
);
498 if (input_card_idr
== IDR_AUTOFILL_CC_GENERIC
) {
499 // When the credit card type is unknown, no image should be shown. However,
500 // to simplify the view code on Mac, save space for the credit card image by
501 // returning a transparent image of the appropriate size.
502 result
= gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage(
503 result
.AsImageSkia(), 0));
508 gfx::Image
CvcIconForCreditCardType(const base::string16
& credit_card_type
) {
509 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
510 if (credit_card_type
== l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX
))
511 return rb
.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT_AMEX
);
513 return rb
.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT
);
518 AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {}
520 AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
521 if (popup_controller_
)
522 popup_controller_
->Hide();
524 GetMetricLogger().LogDialogInitialUserState(initial_user_state_
);
528 base::WeakPtr
<AutofillDialogControllerImpl
>
529 AutofillDialogControllerImpl::Create(
530 content::WebContents
* contents
,
531 const FormData
& form_structure
,
532 const GURL
& source_url
,
533 const base::Callback
<void(const FormStructure
*)>& callback
) {
534 // AutofillDialogControllerImpl owns itself.
535 AutofillDialogControllerImpl
* autofill_dialog_controller
=
536 new AutofillDialogControllerImpl(contents
,
540 return autofill_dialog_controller
->weak_ptr_factory_
.GetWeakPtr();
544 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple
* registry
) {
545 registry
->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance
);
549 void AutofillDialogController::RegisterProfilePrefs(
550 user_prefs::PrefRegistrySyncable
* registry
) {
551 registry
->RegisterBooleanPref(
552 ::prefs::kAutofillDialogPayWithoutWallet
,
554 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
555 registry
->RegisterDictionaryPref(
556 ::prefs::kAutofillDialogAutofillDefault
,
557 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
558 registry
->RegisterBooleanPref(
559 ::prefs::kAutofillDialogSaveData
,
561 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
565 base::WeakPtr
<AutofillDialogController
> AutofillDialogController::Create(
566 content::WebContents
* contents
,
567 const FormData
& form_structure
,
568 const GURL
& source_url
,
569 const base::Callback
<void(const FormStructure
*)>& callback
) {
570 return AutofillDialogControllerImpl::Create(contents
,
576 void AutofillDialogControllerImpl::Show() {
577 dialog_shown_timestamp_
= base::Time::Now();
579 // The Autofill dialog is shown in response to a message from the renderer and
580 // as such, it can only be made in the context of the current document. A call
581 // to GetActiveEntry would return a pending entry, if there was one, which
582 // would be a security bug. Therefore, we use the last committed URL for the
584 const GURL
& current_url
= web_contents()->GetLastCommittedURL();
585 invoked_from_same_origin_
=
586 current_url
.GetOrigin() == source_url_
.GetOrigin();
588 // Log any relevant UI metrics and security exceptions.
589 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN
);
591 GetMetricLogger().LogDialogSecurityMetric(
592 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN
);
594 // Determine what field types should be included in the dialog.
595 // Note that RequestingCreditCardInfo() below relies on parsed field types.
596 bool has_types
= false;
597 bool has_sections
= false;
598 form_structure_
.ParseFieldTypesFromAutocompleteAttributes(
599 &has_types
, &has_sections
);
601 if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
602 GetMetricLogger().LogDialogSecurityMetric(
603 AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP
);
606 if (!invoked_from_same_origin_
) {
607 GetMetricLogger().LogDialogSecurityMetric(
608 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME
);
611 // Fail if the author didn't specify autocomplete types.
618 common::BuildInputsForSection(SECTION_CC
,
619 &requested_cc_fields_
);
620 common::BuildInputsForSection(SECTION_BILLING
,
621 &requested_billing_fields_
);
622 common::BuildInputsForSection(SECTION_CC_BILLING
,
623 &requested_cc_billing_fields_
);
624 common::BuildInputsForSection(SECTION_SHIPPING
,
625 &requested_shipping_fields_
);
627 // Test whether we need to show the shipping section. If filling that section
628 // would be a no-op, don't show it.
629 const DetailInputs
& inputs
= RequestedFieldsForSection(SECTION_SHIPPING
);
630 EmptyDataModelWrapper empty_wrapper
;
631 cares_about_shipping_
= empty_wrapper
.FillFormStructure(
633 base::Bind(common::DetailInputMatchesField
, SECTION_SHIPPING
),
636 SuggestionsUpdated();
637 SubmitButtonDelayBegin();
639 if (account_chooser_model_
.WalletIsSelected())
642 // TODO(estade): don't show the dialog if the site didn't specify the right
643 // fields. First we must figure out what the "right" fields are.
644 view_
.reset(CreateView());
646 GetManager()->AddObserver(this);
648 if (!account_chooser_model_
.WalletIsSelected())
649 LogDialogLatencyToShow();
652 void AutofillDialogControllerImpl::Hide() {
657 void AutofillDialogControllerImpl::TabActivated() {
658 // If the user switched away from this tab and then switched back, reload the
659 // Wallet items, in case they've changed.
660 int64 seconds_elapsed_since_last_refresh
=
661 (base::TimeTicks::Now() - last_wallet_items_fetch_timestamp_
).InSeconds();
662 if (IsPayingWithWallet() && wallet_items_
&&
663 seconds_elapsed_since_last_refresh
>= kWalletItemsRefreshRateSeconds
) {
668 TestableAutofillDialogView
* AutofillDialogControllerImpl::GetTestableView() {
669 return view_
? view_
->GetTestableView() : NULL
;
672 ////////////////////////////////////////////////////////////////////////////////
673 // AutofillDialogViewDelegate implementation.
675 string16
AutofillDialogControllerImpl::DialogTitle() const {
676 if (ShouldShowSpinner())
679 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE
);
682 string16
AutofillDialogControllerImpl::AccountChooserText() const {
683 if (!account_chooser_model_
.WalletIsSelected())
684 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PAYING_WITHOUT_WALLET
);
686 if (SignedInState() == SIGNED_IN
)
687 return account_chooser_model_
.GetActiveWalletAccountName();
689 // In this case, the account chooser should be showing the signin link.
693 string16
AutofillDialogControllerImpl::SignInLinkText() const {
694 int ids
= SignedInState() == NOT_CHECKED
?
695 IDS_AUTOFILL_DIALOG_USE_WALLET_LINK
:
696 ShouldShowSignInWebView() ? IDS_AUTOFILL_DIALOG_CANCEL_SIGN_IN
:
697 IDS_AUTOFILL_DIALOG_SIGN_IN
;
699 return l10n_util::GetStringUTF16(ids
);
702 string16
AutofillDialogControllerImpl::SpinnerText() const {
703 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_LOADING
);
706 string16
AutofillDialogControllerImpl::EditSuggestionText() const {
707 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT
);
710 string16
AutofillDialogControllerImpl::CancelButtonText() const {
711 return l10n_util::GetStringUTF16(IDS_CANCEL
);
714 string16
AutofillDialogControllerImpl::ConfirmButtonText() const {
715 return l10n_util::GetStringUTF16(IsSubmitPausedOn(wallet::VERIFY_CVV
) ?
716 IDS_AUTOFILL_DIALOG_VERIFY_BUTTON
: IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON
);
719 string16
AutofillDialogControllerImpl::SaveLocallyText() const {
720 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX
);
723 string16
AutofillDialogControllerImpl::SaveLocallyTooltip() const {
724 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_TOOLTIP
);
727 string16
AutofillDialogControllerImpl::LegalDocumentsText() {
728 if (!IsPayingWithWallet())
731 return legal_documents_text_
;
734 bool AutofillDialogControllerImpl::ShouldShowSpinner() const {
735 return account_chooser_model_
.WalletIsSelected() &&
736 (SignedInState() == REQUIRES_RESPONSE
||
737 SignedInState() == REQUIRES_PASSIVE_SIGN_IN
);
740 bool AutofillDialogControllerImpl::ShouldShowSignInWebView() const {
741 return !signin_registrar_
.IsEmpty();
744 GURL
AutofillDialogControllerImpl::SignInUrl() const {
745 return wallet::GetSignInUrl();
748 bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
749 return !IsPayingWithWallet() &&
750 !profile_
->IsOffTheRecord() &&
751 IsManuallyEditingAnySection() &&
752 !ShouldShowSpinner();
755 bool AutofillDialogControllerImpl::ShouldSaveInChrome() const {
756 return profile_
->GetPrefs()->GetBoolean(::prefs::kAutofillDialogSaveData
);
759 int AutofillDialogControllerImpl::GetDialogButtons() const {
760 if (waiting_for_explicit_sign_in_response_
)
761 return ui::DIALOG_BUTTON_NONE
;
763 if (ShouldShowSpinner() && !handling_use_wallet_link_click_
)
764 return ui::DIALOG_BUTTON_CANCEL
;
766 return ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
;
769 bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
770 ui::DialogButton button
) const {
771 if (button
== ui::DIALOG_BUTTON_OK
) {
772 if (IsSubmitPausedOn(wallet::VERIFY_CVV
))
775 if (ShouldShowSpinner() || is_submitting_
)
778 if (submit_button_delay_timer_
.IsRunning())
784 DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL
, button
);
785 return !is_submitting_
|| IsSubmitPausedOn(wallet::VERIFY_CVV
);
788 DialogOverlayState
AutofillDialogControllerImpl::GetDialogOverlay() {
789 bool show_wallet_interstitial
= IsPayingWithWallet() && is_submitting_
&&
790 !(full_wallet_
&& !full_wallet_
->required_actions().empty());
791 if (!show_wallet_interstitial
) {
792 card_scrambling_delay_
.Stop();
793 card_scrambling_refresher_
.Stop();
794 return DialogOverlayState();
797 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
798 DialogOverlayState state
;
799 state
.string
.font
= rb
.GetFont(ui::ResourceBundle::BaseFont
).DeriveFont(3);
800 state
.string
.text_color
= SK_ColorDKGRAY
;
802 const SkColor start_top_color
= SkColorSetRGB(0xD6, 0xD6, 0xD6);
803 const SkColor start_bottom_color
= SkColorSetRGB(0x98, 0x98, 0x98);
804 const SkColor final_top_color
= SkColorSetRGB(0x52, 0x9F, 0xF8);
805 const SkColor final_bottom_color
= SkColorSetRGB(0x22, 0x75, 0xE5);
807 if (full_wallet_
&& full_wallet_
->required_actions().empty()) {
808 card_scrambling_delay_
.Stop();
809 card_scrambling_refresher_
.Stop();
812 full_wallet_
->GetInfo(AutofillType(CREDIT_CARD_NUMBER
));
813 DCHECK_GE(cc_number
.size(), 4U);
814 state
.image
= GetGeneratedCardImage(
815 ASCIIToUTF16("XXXX XXXX XXXX ") +
816 cc_number
.substr(cc_number
.size() - 4),
817 full_wallet_
->billing_address()->recipient_name(),
818 color_utils::AlphaBlend(
821 255 * card_generated_animation_
.GetCurrentValue()),
822 color_utils::AlphaBlend(
825 255 * card_generated_animation_
.GetCurrentValue()));
827 state
.string
.text
= l10n_util::GetStringUTF16(
828 IDS_AUTOFILL_DIALOG_CARD_GENERATION_DONE
);
830 // Start the refresher if it isn't running. Wait one second before pumping
831 // updates to the view.
832 if (!card_scrambling_delay_
.IsRunning() &&
833 !card_scrambling_refresher_
.IsRunning()) {
834 scrambled_card_number_
= GenerateRandomCardNumber();
835 card_scrambling_delay_
.Start(
837 base::TimeDelta::FromSeconds(1),
839 &AutofillDialogControllerImpl::StartCardScramblingRefresher
);
842 DCHECK(!scrambled_card_number_
.empty());
843 state
.image
= GetGeneratedCardImage(
844 scrambled_card_number_
,
845 submitted_cardholder_name_
,
849 // "Submitting" waiting page.
850 state
.string
.text
= l10n_util::GetStringUTF16(
851 IDS_AUTOFILL_DIALOG_CARD_GENERATION_IN_PROGRESS
);
857 const std::vector
<gfx::Range
>& AutofillDialogControllerImpl::
858 LegalDocumentLinks() {
859 return legal_document_link_ranges_
;
862 bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section
)
864 if (IsSubmitPausedOn(wallet::VERIFY_CVV
))
865 return section
== SECTION_CC_BILLING
;
867 if (!FormStructureCaresAboutSection(section
))
870 if (IsPayingWithWallet())
871 return section
== SECTION_CC_BILLING
|| section
== SECTION_SHIPPING
;
873 return section
!= SECTION_CC_BILLING
;
876 void AutofillDialogControllerImpl::GetWalletItems() {
877 ScopedViewUpdates
updates(view_
.get());
879 wallet_items_requested_
= true;
881 previously_selected_instrument_id_
.clear();
882 previously_selected_shipping_address_id_
.clear();
884 previous_default_instrument_id_
= wallet_items_
->default_instrument_id();
885 previous_default_shipping_address_id_
= wallet_items_
->default_address_id();
887 const wallet::WalletItems::MaskedInstrument
* instrument
=
890 previously_selected_instrument_id_
= instrument
->object_id();
892 const wallet::Address
* address
= ActiveShippingAddress();
894 previously_selected_shipping_address_id_
= address
->object_id();
897 last_wallet_items_fetch_timestamp_
= base::TimeTicks::Now();
898 wallet_items_
.reset();
900 // The "Loading..." page should be showing now, which should cause the
901 // account chooser to hide.
902 view_
->UpdateAccountChooser();
903 GetWalletClient()->GetWalletItems();
906 void AutofillDialogControllerImpl::HideSignIn() {
907 ScopedViewUpdates
updates(view_
.get());
908 signin_registrar_
.RemoveAll();
910 view_
->UpdateAccountChooser();
913 AutofillDialogControllerImpl::DialogSignedInState
914 AutofillDialogControllerImpl::SignedInState() const {
915 if (wallet_error_notification_
)
916 return SIGN_IN_DISABLED
;
918 if (signin_helper_
|| (wallet_items_requested_
&& !wallet_items_
))
919 return REQUIRES_RESPONSE
;
921 if (!wallet_items_requested_
)
924 if (wallet_items_
->HasRequiredAction(wallet::GAIA_AUTH
))
925 return REQUIRES_SIGN_IN
;
927 if (wallet_items_
->HasRequiredAction(wallet::PASSIVE_GAIA_AUTH
))
928 return REQUIRES_PASSIVE_SIGN_IN
;
933 void AutofillDialogControllerImpl::SignedInStateUpdated() {
934 if (!ShouldShowSpinner())
935 waiting_for_explicit_sign_in_response_
= false;
937 switch (SignedInState()) {
939 LogDialogLatencyToShow();
942 case REQUIRES_SIGN_IN
:
943 if (handling_use_wallet_link_click_
)
946 case SIGN_IN_DISABLED
:
947 // Switch to the local account and refresh the dialog.
948 signin_helper_
.reset();
949 OnWalletSigninError();
950 handling_use_wallet_link_click_
= false;
953 case REQUIRES_PASSIVE_SIGN_IN
:
954 // Attempt to passively sign in the user.
955 DCHECK(!signin_helper_
);
956 signin_helper_
.reset(new wallet::WalletSigninHelper(
958 profile_
->GetRequestContext()));
959 signin_helper_
->StartPassiveSignin();
963 case REQUIRES_RESPONSE
:
968 void AutofillDialogControllerImpl::OnWalletOrSigninUpdate() {
969 ScopedViewUpdates
updates(view_
.get());
970 SignedInStateUpdated();
971 SuggestionsUpdated();
972 UpdateAccountChooserView();
975 view_
->UpdateButtonStrip();
976 view_
->UpdateOverlay();
979 // On the first successful response, compute the initial user state metric.
980 if (initial_user_state_
== AutofillMetrics::DIALOG_USER_STATE_UNKNOWN
)
981 initial_user_state_
= GetInitialUserState();
984 void AutofillDialogControllerImpl::OnWalletFormFieldError(
985 const std::vector
<wallet::FormFieldError
>& form_field_errors
) {
986 if (form_field_errors
.empty())
989 for (std::vector
<wallet::FormFieldError
>::const_iterator it
=
990 form_field_errors
.begin();
991 it
!= form_field_errors
.end(); ++it
) {
992 if (it
->error_type() == wallet::FormFieldError::UNKNOWN_ERROR
||
993 it
->GetAutofillType() == MAX_VALID_FIELD_TYPE
||
994 it
->location() == wallet::FormFieldError::UNKNOWN_LOCATION
) {
995 wallet_server_validation_recoverable_
= false;
998 DialogSection section
= SectionFromLocation(it
->location());
999 wallet_errors_
[section
][it
->GetAutofillType()] =
1000 std::make_pair(it
->GetErrorMessage(),
1001 GetValueFromSection(section
, it
->GetAutofillType()));
1004 // Unrecoverable validation errors.
1005 if (!wallet_server_validation_recoverable_
)
1006 DisableWallet(wallet::WalletClient::UNKNOWN_ERROR
);
1011 void AutofillDialogControllerImpl::ConstructLegalDocumentsText() {
1012 legal_documents_text_
.clear();
1013 legal_document_link_ranges_
.clear();
1018 PrefService
* local_state
= g_browser_process
->local_state();
1019 // List of users who have accepted location sharing for fraud protection
1021 const base::ListValue
* accepted
=
1022 local_state
->GetList(::prefs::kAutofillDialogWalletLocationAcceptance
);
1023 bool has_accepted_location_sharing
=
1024 accepted
->Find(base::StringValue(
1025 account_chooser_model_
.GetActiveWalletAccountName())) !=
1028 if (wallet_items_
->legal_documents().empty()) {
1029 if (!has_accepted_location_sharing
) {
1030 legal_documents_text_
= l10n_util::GetStringUTF16(
1031 IDS_AUTOFILL_DIALOG_LOCATION_DISCLOSURE
);
1037 const std::vector
<wallet::WalletItems::LegalDocument
*>& documents
=
1038 wallet_items_
->legal_documents();
1039 // There should never be just one document because the privacy policy doc gets
1040 // tacked on the end of other documents.
1041 DCHECK_GE(documents
.size(), 2U);
1043 std::vector
<base::string16
> link_names
;
1044 for (size_t i
= 0; i
< documents
.size(); ++i
) {
1045 link_names
.push_back(documents
[i
]->display_name());
1048 // TODO(estade): Replace this with a more permanent solution. See
1049 // http://crbug.com/319049 and http://crbug.com/316045
1050 const bool new_user
= true;
1051 int resource_id
= 0;
1052 switch (documents
.size()) {
1054 resource_id
= new_user
? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_2
:
1055 IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_2
;
1058 resource_id
= new_user
? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_3
:
1059 IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_3
;
1062 resource_id
= new_user
? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_4
:
1063 IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_4
;
1066 resource_id
= new_user
? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_5
:
1067 IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_5
;
1070 resource_id
= new_user
? IDS_AUTOFILL_DIALOG_LEGAL_LINKS_NEW_6
:
1071 IDS_AUTOFILL_DIALOG_LEGAL_LINKS_UPDATED_6
;
1074 // We can only handle so many documents. For lack of a better way of
1075 // handling document overflow, just error out if there are too many.
1076 DisableWallet(wallet::WalletClient::UNKNOWN_ERROR
);
1080 std::vector
<size_t> offsets
;
1081 string16 text
= l10n_util::GetStringFUTF16(resource_id
, link_names
, &offsets
);
1083 // Tack on the location string if need be.
1084 size_t base_offset
= 0;
1085 if (!has_accepted_location_sharing
) {
1086 text
= l10n_util::GetStringFUTF16(
1087 IDS_AUTOFILL_DIALOG_LOCATION_DISCLOSURE_WITH_LEGAL_DOCS
,
1092 for (size_t i
= 0; i
< documents
.size(); ++i
) {
1093 size_t link_start
= offsets
[i
] + base_offset
;
1094 legal_document_link_ranges_
.push_back(gfx::Range(
1095 link_start
, link_start
+ documents
[i
]->display_name().size()));
1097 legal_documents_text_
= text
;
1100 void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section
) {
1101 SetEditingExistingData(section
, false);
1103 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
1104 for (DetailInputs::iterator it
= inputs
->begin(); it
!= inputs
->end(); ++it
) {
1105 it
->initial_value
= common::GetHardcodedValueForType(it
->type
);
1109 void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion(
1110 DialogSection section
) {
1111 // |CreateWrapper()| returns an empty wrapper if |IsEditingExistingData()|, so
1112 // get the wrapper before this potentially happens below.
1113 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
1115 // If the chosen item in |model| yields an empty suggestion text, it is
1116 // invalid. In this case, show the edit UI and highlight invalid fields.
1117 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1118 string16 unused
, unused2
;
1119 if (IsASuggestionItemKey(model
->GetItemKeyForCheckedItem()) &&
1120 !SuggestionTextForSection(section
, &unused
, &unused2
)) {
1121 SetEditingExistingData(section
, true);
1124 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
1125 if (wrapper
&& IsEditingExistingData(section
))
1126 wrapper
->FillInputs(inputs
);
1129 bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type
,
1130 const base::string16
& value
) {
1134 // If this is a combobox at the default value, don't preserve it.
1135 ui::ComboboxModel
* model
= ComboboxModelForAutofillType(type
);
1136 if (model
&& model
->GetItemAt(model
->GetDefaultIndex()) == value
)
1142 DetailOutputMap
AutofillDialogControllerImpl::TakeUserInputSnapshot() {
1143 DetailOutputMap snapshot
;
1147 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1148 DialogSection section
= static_cast<DialogSection
>(i
);
1149 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1150 if (model
->GetItemKeyForCheckedItem() != kAddNewItemKey
)
1153 DetailOutputMap outputs
;
1154 view_
->GetUserInput(section
, &outputs
);
1155 // Remove fields that are empty, at their default values, or invalid.
1156 for (DetailOutputMap::iterator it
= outputs
.begin(); it
!= outputs
.end();
1158 if (InputWasEdited(it
->first
->type
, it
->second
) &&
1159 InputValidityMessage(section
, it
->first
->type
, it
->second
).empty()) {
1160 snapshot
.insert(std::make_pair(it
->first
, it
->second
));
1168 void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
1169 const DetailOutputMap
& snapshot
) {
1170 if (snapshot
.empty())
1173 DetailOutputWrapper
wrapper(snapshot
);
1174 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1175 DialogSection section
= static_cast<DialogSection
>(i
);
1176 if (!SectionIsActive(section
))
1179 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
1180 wrapper
.FillInputs(inputs
);
1182 for (size_t i
= 0; i
< inputs
->size(); ++i
) {
1183 if (InputWasEdited((*inputs
)[i
].type
, (*inputs
)[i
].initial_value
)) {
1184 SuggestionsMenuModelForSection(section
)->SetCheckedItem(kAddNewItemKey
);
1191 void AutofillDialogControllerImpl::UpdateSection(DialogSection section
) {
1193 view_
->UpdateSection(section
);
1196 void AutofillDialogControllerImpl::UpdateForErrors() {
1200 // Currently, the view should only need to be updated if there are
1201 // |wallet_errors_| or validating a suggestion that's based on existing data.
1202 bool should_update
= !wallet_errors_
.empty();
1203 if (!should_update
) {
1204 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1205 if (IsEditingExistingData(static_cast<DialogSection
>(i
))) {
1206 should_update
= true;
1213 view_
->UpdateForErrors();
1216 gfx::Image
AutofillDialogControllerImpl::GetGeneratedCardImage(
1217 const base::string16
& card_number
,
1218 const base::string16
& name
,
1219 const SkColor
& gradient_top
,
1220 const SkColor
& gradient_bottom
) {
1221 const int kCardWidthPx
= 300;
1222 const int kCardHeightPx
= 190;
1223 const gfx::Size
size(kCardWidthPx
, kCardHeightPx
);
1224 ui::ScaleFactor scale_factor
= ui::GetScaleFactorForNativeView(
1225 web_contents()->GetView()->GetNativeView());
1226 gfx::Canvas
canvas(size
, ui::GetImageScale(scale_factor
), false);
1228 gfx::Rect
display_rect(size
);
1230 skia::RefPtr
<SkShader
> shader
= gfx::CreateGradientShader(
1231 0, size
.height(), gradient_top
, gradient_bottom
);
1233 paint
.setShader(shader
.get());
1234 canvas
.DrawRoundRect(display_rect
, 8, paint
);
1236 display_rect
.Inset(20, 0, 0, 0);
1237 gfx::Font
font(l10n_util::GetStringUTF8(IDS_FIXED_FONT_FAMILY
), 18);
1238 gfx::FontList
font_list(font
);
1239 gfx::ShadowValues shadows
;
1240 shadows
.push_back(gfx::ShadowValue(gfx::Point(0, 1), 1.0, SK_ColorBLACK
));
1241 canvas
.DrawStringRectWithShadows(
1245 display_rect
, 0, 0, shadows
);
1247 base::string16 capitalized_name
= base::i18n::ToUpper(name
);
1248 display_rect
.Inset(0, size
.height() / 2, 0, 0);
1249 canvas
.DrawStringRectWithShadows(
1253 display_rect
, 0, 0, shadows
);
1255 gfx::ImageSkia
skia(canvas
.ExtractImageRep());
1256 return gfx::Image(skia
);
1259 void AutofillDialogControllerImpl::StartCardScramblingRefresher() {
1260 RefreshCardScramblingOverlay();
1261 card_scrambling_refresher_
.Start(
1263 base::TimeDelta::FromMilliseconds(75),
1265 &AutofillDialogControllerImpl::RefreshCardScramblingOverlay
);
1268 void AutofillDialogControllerImpl::RefreshCardScramblingOverlay() {
1269 scrambled_card_number_
= GenerateRandomCardNumber();
1270 PushOverlayUpdate();
1273 void AutofillDialogControllerImpl::PushOverlayUpdate() {
1275 ScopedViewUpdates
updates(view_
.get());
1276 view_
->UpdateOverlay();
1280 const DetailInputs
& AutofillDialogControllerImpl::RequestedFieldsForSection(
1281 DialogSection section
) const {
1284 return requested_cc_fields_
;
1285 case SECTION_BILLING
:
1286 return requested_billing_fields_
;
1287 case SECTION_CC_BILLING
:
1288 return requested_cc_billing_fields_
;
1289 case SECTION_SHIPPING
:
1290 return requested_shipping_fields_
;
1294 return requested_billing_fields_
;
1297 ui::ComboboxModel
* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
1298 ServerFieldType type
) {
1300 case CREDIT_CARD_EXP_MONTH
:
1301 return &cc_exp_month_combobox_model_
;
1303 case CREDIT_CARD_EXP_4_DIGIT_YEAR
:
1304 return &cc_exp_year_combobox_model_
;
1306 case ADDRESS_HOME_COUNTRY
:
1307 case ADDRESS_BILLING_COUNTRY
:
1308 return &country_combobox_model_
;
1315 ui::MenuModel
* AutofillDialogControllerImpl::MenuModelForSection(
1316 DialogSection section
) {
1317 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1318 // The shipping section menu is special. It will always show because there is
1319 // a choice between "Use billing" and "enter new".
1320 if (section
== SECTION_SHIPPING
)
1323 // For other sections, only show a menu if there's at least one suggestion.
1324 for (int i
= 0; i
< model
->GetItemCount(); ++i
) {
1325 if (IsASuggestionItemKey(model
->GetItemKeyAt(i
)))
1332 ui::MenuModel
* AutofillDialogControllerImpl::MenuModelForAccountChooser() {
1333 // If there were unrecoverable Wallet errors, or if there are choices other
1334 // than "Pay without the wallet", show the full menu.
1335 // TODO(estade): this can present a braindead menu (only 1 option) when
1336 // there's a wallet error.
1337 if (wallet_error_notification_
||
1338 (SignedInState() == SIGNED_IN
&&
1339 account_chooser_model_
.HasAccountsToChoose())) {
1340 return &account_chooser_model_
;
1343 // Otherwise, there is no menu, just a sign in link.
1347 gfx::Image
AutofillDialogControllerImpl::AccountChooserImage() {
1348 if (!MenuModelForAccountChooser() && !ShouldShowSignInWebView()) {
1349 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1353 return gfx::Image();
1356 gfx::Image
AutofillDialogControllerImpl::ButtonStripImage() const {
1357 if (IsPayingWithWallet()) {
1358 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1362 return gfx::Image();
1365 string16
AutofillDialogControllerImpl::LabelForSection(DialogSection section
)
1369 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC
);
1370 case SECTION_BILLING
:
1371 case SECTION_CC_BILLING
:
1372 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING
);
1373 case SECTION_SHIPPING
:
1374 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING
);
1380 SuggestionState
AutofillDialogControllerImpl::SuggestionStateForSection(
1381 DialogSection section
) {
1382 string16 vertically_compact
, horizontally_compact
;
1383 bool show_suggestion
= SuggestionTextForSection(section
,
1384 &vertically_compact
,
1385 &horizontally_compact
);
1386 return SuggestionState(show_suggestion
,
1388 horizontally_compact
,
1389 SuggestionIconForSection(section
),
1390 ExtraSuggestionTextForSection(section
),
1391 ExtraSuggestionIconForSection(section
));
1394 bool AutofillDialogControllerImpl::SuggestionTextForSection(
1395 DialogSection section
,
1396 base::string16
* vertically_compact
,
1397 base::string16
* horizontally_compact
) {
1398 base::string16 action_text
= RequiredActionTextForSection(section
);
1399 if (!action_text
.empty()) {
1400 *vertically_compact
= *horizontally_compact
= action_text
;
1404 // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a
1405 // user selects a credit card that has expired), don't show a suggestion (even
1406 // though there is a profile selected in the model).
1407 if (IsEditingExistingData(section
))
1410 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1411 std::string item_key
= model
->GetItemKeyForCheckedItem();
1412 if (item_key
== kSameAsBillingKey
) {
1413 *vertically_compact
= *horizontally_compact
= l10n_util::GetStringUTF16(
1414 IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING
);
1418 if (!IsASuggestionItemKey(item_key
))
1421 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
1422 return wrapper
->GetDisplayText(vertically_compact
, horizontally_compact
);
1425 string16
AutofillDialogControllerImpl::RequiredActionTextForSection(
1426 DialogSection section
) const {
1427 if (section
== SECTION_CC_BILLING
&& IsSubmitPausedOn(wallet::VERIFY_CVV
)) {
1428 const wallet::WalletItems::MaskedInstrument
* current_instrument
=
1429 wallet_items_
->GetInstrumentById(active_instrument_id_
);
1430 if (current_instrument
)
1431 return current_instrument
->TypeAndLastFourDigits();
1433 DetailOutputMap output
;
1434 view_
->GetUserInput(section
, &output
);
1436 GetBillingInfoFromOutputs(output
, &card
, NULL
, NULL
);
1437 return card
.TypeAndLastFourDigits();
1443 string16
AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
1444 DialogSection section
) const {
1445 if (section
== SECTION_CC
||
1446 (section
== SECTION_CC_BILLING
&& IsSubmitPausedOn(wallet::VERIFY_CVV
))) {
1447 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC
);
1453 const wallet::WalletItems::MaskedInstrument
* AutofillDialogControllerImpl::
1454 ActiveInstrument() const {
1455 if (!IsPayingWithWallet())
1458 const SuggestionsMenuModel
* model
=
1459 SuggestionsMenuModelForSection(SECTION_CC_BILLING
);
1460 const std::string item_key
= model
->GetItemKeyForCheckedItem();
1461 if (!IsASuggestionItemKey(item_key
))
1465 if (!base::StringToInt(item_key
, &index
) || index
< 0 ||
1466 static_cast<size_t>(index
) >= wallet_items_
->instruments().size()) {
1471 return wallet_items_
->instruments()[index
];
1474 const wallet::Address
* AutofillDialogControllerImpl::
1475 ActiveShippingAddress() const {
1476 if (!IsPayingWithWallet() || !IsShippingAddressRequired())
1479 const SuggestionsMenuModel
* model
=
1480 SuggestionsMenuModelForSection(SECTION_SHIPPING
);
1481 const std::string item_key
= model
->GetItemKeyForCheckedItem();
1482 if (!IsASuggestionItemKey(item_key
))
1486 if (!base::StringToInt(item_key
, &index
) || index
< 0 ||
1487 static_cast<size_t>(index
) >= wallet_items_
->addresses().size()) {
1492 return wallet_items_
->addresses()[index
];
1495 scoped_ptr
<DataModelWrapper
> AutofillDialogControllerImpl::CreateWrapper(
1496 DialogSection section
) {
1497 if (IsPayingWithWallet() && full_wallet_
&&
1498 full_wallet_
->required_actions().empty()) {
1499 if (section
== SECTION_CC_BILLING
) {
1500 return scoped_ptr
<DataModelWrapper
>(
1501 new FullWalletBillingWrapper(full_wallet_
.get()));
1503 if (section
== SECTION_SHIPPING
) {
1504 return scoped_ptr
<DataModelWrapper
>(
1505 new FullWalletShippingWrapper(full_wallet_
.get()));
1509 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1510 std::string item_key
= model
->GetItemKeyForCheckedItem();
1511 if (!IsASuggestionItemKey(item_key
) || IsManuallyEditingSection(section
))
1512 return scoped_ptr
<DataModelWrapper
>();
1514 if (IsPayingWithWallet()) {
1515 if (section
== SECTION_CC_BILLING
) {
1516 return scoped_ptr
<DataModelWrapper
>(
1517 new WalletInstrumentWrapper(ActiveInstrument()));
1520 if (section
== SECTION_SHIPPING
) {
1521 return scoped_ptr
<DataModelWrapper
>(
1522 new WalletAddressWrapper(ActiveShippingAddress()));
1525 return scoped_ptr
<DataModelWrapper
>();
1528 if (section
== SECTION_CC
) {
1529 CreditCard
* card
= GetManager()->GetCreditCardByGUID(item_key
);
1531 return scoped_ptr
<DataModelWrapper
>(new AutofillCreditCardWrapper(card
));
1534 AutofillProfile
* profile
= GetManager()->GetProfileByGUID(item_key
);
1536 if (section
== SECTION_SHIPPING
) {
1537 return scoped_ptr
<DataModelWrapper
>(
1538 new AutofillShippingAddressWrapper(profile
));
1540 DCHECK_EQ(SECTION_BILLING
, section
);
1541 return scoped_ptr
<DataModelWrapper
>(
1542 new AutofillProfileWrapper(profile
));
1545 gfx::Image
AutofillDialogControllerImpl::SuggestionIconForSection(
1546 DialogSection section
) {
1547 scoped_ptr
<DataModelWrapper
> model
= CreateWrapper(section
);
1549 return gfx::Image();
1551 return model
->GetIcon();
1554 gfx::Image
AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
1555 DialogSection section
) {
1556 if (section
!= SECTION_CC
&& section
!= SECTION_CC_BILLING
)
1557 return gfx::Image();
1559 scoped_ptr
<DataModelWrapper
> model
= CreateWrapper(section
);
1561 return gfx::Image();
1563 return CvcIconForCreditCardType(
1564 model
->GetInfo(AutofillType(CREDIT_CARD_TYPE
)));
1567 FieldIconMap
AutofillDialogControllerImpl::IconsForFields(
1568 const FieldValueMap
& user_inputs
) const {
1569 FieldIconMap result
;
1570 base::string16 credit_card_type
;
1572 FieldValueMap::const_iterator credit_card_iter
=
1573 user_inputs
.find(CREDIT_CARD_NUMBER
);
1574 if (credit_card_iter
!= user_inputs
.end()) {
1575 const string16
& number
= credit_card_iter
->second
;
1576 const std::string type
= CreditCard::GetCreditCardType(number
);
1577 credit_card_type
= CreditCard::TypeForDisplay(type
);
1578 result
[CREDIT_CARD_NUMBER
] = CreditCardIconForType(type
);
1581 if (!user_inputs
.count(CREDIT_CARD_VERIFICATION_CODE
))
1584 result
[CREDIT_CARD_VERIFICATION_CODE
] =
1585 CvcIconForCreditCardType(credit_card_type
);
1590 bool AutofillDialogControllerImpl::FieldControlsIcons(
1591 ServerFieldType type
) const {
1592 return type
== CREDIT_CARD_NUMBER
;
1595 string16
AutofillDialogControllerImpl::TooltipForField(ServerFieldType type
)
1597 if (type
== PHONE_HOME_WHOLE_NUMBER
|| type
== PHONE_BILLING_WHOLE_NUMBER
)
1598 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TOOLTIP_PHONE_NUMBER
);
1603 bool AutofillDialogControllerImpl::InputIsEditable(
1604 const DetailInput
& input
,
1605 DialogSection section
) {
1606 if (section
!= SECTION_CC_BILLING
)
1609 if (input
.type
== CREDIT_CARD_NUMBER
)
1610 return !IsEditingExistingData(section
);
1612 // For CVC, only require (allow) input if the user has edited some other
1613 // aspect of the card.
1614 if (input
.type
== CREDIT_CARD_VERIFICATION_CODE
&&
1615 IsEditingExistingData(section
)) {
1616 DetailOutputMap output
;
1617 view_
->GetUserInput(section
, &output
);
1618 WalletInstrumentWrapper
wrapper(ActiveInstrument());
1620 for (DetailOutputMap::iterator iter
= output
.begin(); iter
!= output
.end();
1622 if (iter
->first
->type
== input
.type
)
1625 AutofillType
type(iter
->first
->type
);
1626 if (type
.group() == CREDIT_CARD
&&
1627 iter
->second
!= wrapper
.GetInfo(type
)) {
1638 // TODO(groby): Add more tests.
1639 string16
AutofillDialogControllerImpl::InputValidityMessage(
1640 DialogSection section
,
1641 ServerFieldType type
,
1642 const string16
& value
) {
1643 // If the field is edited, clear any Wallet errors.
1644 if (IsPayingWithWallet()) {
1645 WalletValidationErrors::iterator it
= wallet_errors_
.find(section
);
1646 if (it
!= wallet_errors_
.end()) {
1647 TypeErrorInputMap::const_iterator iter
= it
->second
.find(type
);
1648 if (iter
!= it
->second
.end()) {
1649 if (iter
->second
.second
== value
)
1650 return iter
->second
.first
;
1651 it
->second
.erase(type
);
1656 switch (AutofillType(type
).GetStorableType()) {
1658 if (!value
.empty() && !IsValidEmailAddress(value
)) {
1659 return l10n_util::GetStringUTF16(
1660 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS
);
1664 case CREDIT_CARD_NUMBER
: {
1665 if (!value
.empty()) {
1666 base::string16 message
= CreditCardNumberValidityMessage(value
);
1667 if (!message
.empty())
1673 case CREDIT_CARD_EXP_MONTH
:
1674 if (!InputWasEdited(CREDIT_CARD_EXP_MONTH
, value
)) {
1675 return l10n_util::GetStringUTF16(
1676 IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE
);
1680 case CREDIT_CARD_EXP_4_DIGIT_YEAR
:
1681 if (!InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR
, value
)) {
1682 return l10n_util::GetStringUTF16(
1683 IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE
);
1687 case CREDIT_CARD_VERIFICATION_CODE
:
1688 if (!value
.empty() && !autofill::IsValidCreditCardSecurityCode(value
)) {
1689 return l10n_util::GetStringUTF16(
1690 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE
);
1694 case ADDRESS_HOME_LINE1
:
1697 case ADDRESS_HOME_LINE2
:
1698 return base::string16(); // Line 2 is optional - always valid.
1700 case ADDRESS_HOME_CITY
:
1701 case ADDRESS_HOME_COUNTRY
:
1704 case ADDRESS_HOME_STATE
:
1705 if (!value
.empty() && !autofill::IsValidState(value
)) {
1706 return l10n_util::GetStringUTF16(
1707 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_REGION
);
1711 case ADDRESS_HOME_ZIP
:
1712 if (!value
.empty() && !autofill::IsValidZip(value
)) {
1713 return l10n_util::GetStringUTF16(
1714 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_ZIP_CODE
);
1719 // Wallet requires a first and last billing name.
1720 if (section
== SECTION_CC_BILLING
&& !value
.empty() &&
1721 !IsCardHolderNameValidForWallet(value
)) {
1722 DCHECK(IsPayingWithWallet());
1723 return l10n_util::GetStringUTF16(
1724 IDS_AUTOFILL_DIALOG_VALIDATION_WALLET_REQUIRES_TWO_NAMES
);
1728 case PHONE_HOME_WHOLE_NUMBER
: // Used in shipping section.
1731 case PHONE_BILLING_WHOLE_NUMBER
: // Used in billing section.
1735 NOTREACHED(); // Trying to validate unknown field.
1739 return value
.empty() ?
1740 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE
) :
1744 // TODO(groby): Also add tests.
1745 ValidityMessages
AutofillDialogControllerImpl::InputsAreValid(
1746 DialogSection section
,
1747 const DetailOutputMap
& inputs
) {
1748 ValidityMessages messages
;
1749 std::map
<ServerFieldType
, string16
> field_values
;
1750 for (DetailOutputMap::const_iterator iter
= inputs
.begin();
1751 iter
!= inputs
.end(); ++iter
) {
1752 const ServerFieldType type
= iter
->first
->type
;
1754 base::string16 text
= InputValidityMessage(section
, type
, iter
->second
);
1756 // Skip empty/unchanged fields in edit mode. Ignore country code as it
1757 // always has a value. If the individual field does not have validation
1758 // errors, assume it to be valid unless later proven otherwise.
1759 bool sure
= InputWasEdited(type
, iter
->second
) ||
1760 ComboboxModelForAutofillType(type
) == &country_combobox_model_
;
1762 // Consider only individually valid fields for inter-field validation.
1764 field_values
[type
] = iter
->second
;
1765 // If the field is valid but can be invalidated by inter-field validation,
1766 // assume it to be unsure.
1767 if (type
== CREDIT_CARD_EXP_4_DIGIT_YEAR
||
1768 type
== CREDIT_CARD_EXP_MONTH
||
1769 type
== CREDIT_CARD_VERIFICATION_CODE
||
1770 type
== PHONE_HOME_WHOLE_NUMBER
||
1771 type
== PHONE_BILLING_WHOLE_NUMBER
) {
1775 messages
.Set(type
, ValidityMessage(text
, sure
));
1778 // Validate the date formed by month and year field. (Autofill dialog is
1779 // never supposed to have 2-digit years, so not checked).
1780 if (field_values
.count(CREDIT_CARD_EXP_4_DIGIT_YEAR
) &&
1781 field_values
.count(CREDIT_CARD_EXP_MONTH
) &&
1782 InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR
,
1783 field_values
[CREDIT_CARD_EXP_4_DIGIT_YEAR
]) &&
1784 InputWasEdited(CREDIT_CARD_EXP_MONTH
,
1785 field_values
[CREDIT_CARD_EXP_MONTH
])) {
1786 ValidityMessage
year_message(base::string16(), true);
1787 ValidityMessage
month_message(base::string16(), true);
1788 if (!IsCreditCardExpirationValid(field_values
[CREDIT_CARD_EXP_4_DIGIT_YEAR
],
1789 field_values
[CREDIT_CARD_EXP_MONTH
])) {
1790 // The dialog shows the same error message for the month and year fields.
1791 year_message
.text
= l10n_util::GetStringUTF16(
1792 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE
);
1793 month_message
.text
= l10n_util::GetStringUTF16(
1794 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE
);
1796 messages
.Set(CREDIT_CARD_EXP_4_DIGIT_YEAR
, year_message
);
1797 messages
.Set(CREDIT_CARD_EXP_MONTH
, month_message
);
1800 // If there is a credit card number and a CVC, validate them together.
1801 if (field_values
.count(CREDIT_CARD_NUMBER
) &&
1802 field_values
.count(CREDIT_CARD_VERIFICATION_CODE
)) {
1803 ValidityMessage
ccv_message(base::string16(), true);
1804 if (!autofill::IsValidCreditCardSecurityCode(
1805 field_values
[CREDIT_CARD_VERIFICATION_CODE
],
1806 field_values
[CREDIT_CARD_NUMBER
])) {
1807 ccv_message
.text
= l10n_util::GetStringUTF16(
1808 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE
);
1810 messages
.Set(CREDIT_CARD_VERIFICATION_CODE
, ccv_message
);
1813 // Validate the shipping phone number against the country code of the address.
1814 if (field_values
.count(ADDRESS_HOME_COUNTRY
) &&
1815 field_values
.count(PHONE_HOME_WHOLE_NUMBER
)) {
1816 i18n::PhoneObject
phone_object(
1817 field_values
[PHONE_HOME_WHOLE_NUMBER
],
1818 AutofillCountry::GetCountryCode(
1819 field_values
[ADDRESS_HOME_COUNTRY
],
1820 g_browser_process
->GetApplicationLocale()));
1821 ValidityMessage
phone_message(base::string16(), true);
1822 if (!phone_object
.IsValidNumber()) {
1823 phone_message
.text
= l10n_util::GetStringUTF16(
1824 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER
);
1826 messages
.Set(PHONE_HOME_WHOLE_NUMBER
, phone_message
);
1829 // Validate the billing phone number against the country code of the address.
1830 if (field_values
.count(ADDRESS_BILLING_COUNTRY
) &&
1831 field_values
.count(PHONE_BILLING_WHOLE_NUMBER
)) {
1832 i18n::PhoneObject
phone_object(
1833 field_values
[PHONE_BILLING_WHOLE_NUMBER
],
1834 AutofillCountry::GetCountryCode(
1835 field_values
[ADDRESS_BILLING_COUNTRY
],
1836 g_browser_process
->GetApplicationLocale()));
1837 ValidityMessage
phone_message(base::string16(), true);
1838 if (!phone_object
.IsValidNumber()) {
1839 phone_message
.text
= l10n_util::GetStringUTF16(
1840 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER
);
1842 messages
.Set(PHONE_BILLING_WHOLE_NUMBER
, phone_message
);
1848 void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1849 DialogSection section
,
1850 const DetailInput
* input
,
1851 gfx::NativeView parent_view
,
1852 const gfx::Rect
& content_bounds
,
1853 const string16
& field_contents
,
1855 // If the field is edited down to empty, don't show a popup.
1856 if (was_edit
&& field_contents
.empty()) {
1861 // If the user clicks while the popup is already showing, be sure to hide
1863 if (!was_edit
&& popup_controller_
.get()) {
1868 std::vector
<string16
> popup_values
, popup_labels
, popup_icons
;
1869 if (common::IsCreditCardType(input
->type
)) {
1870 GetManager()->GetCreditCardSuggestions(AutofillType(input
->type
),
1877 std::vector
<ServerFieldType
> field_types
;
1878 const DetailInputs
& inputs
= RequestedFieldsForSection(section
);
1879 for (DetailInputs::const_iterator iter
= inputs
.begin();
1880 iter
!= inputs
.end(); ++iter
) {
1881 field_types
.push_back(iter
->type
);
1883 GetManager()->GetProfileSuggestions(AutofillType(input
->type
),
1893 if (popup_values
.empty()) {
1898 // TODO(estade): do we need separators and control rows like 'Clear
1900 std::vector
<int> popup_ids
;
1901 for (size_t i
= 0; i
< popup_guids_
.size(); ++i
) {
1902 popup_ids
.push_back(i
);
1905 popup_controller_
= AutofillPopupControllerImpl::GetOrCreate(
1907 weak_ptr_factory_
.GetWeakPtr(),
1911 base::i18n::IsRTL() ?
1912 base::i18n::RIGHT_TO_LEFT
: base::i18n::LEFT_TO_RIGHT
);
1913 popup_controller_
->set_hide_on_outside_click(true);
1915 // |input_showing_popup_| must be set before calling |Show()|.
1916 input_showing_popup_
= input
;
1918 popup_controller_
->Show(popup_values
,
1924 void AutofillDialogControllerImpl::FocusMoved() {
1928 bool AutofillDialogControllerImpl::ShouldShowErrorBubble() const {
1929 return !input_showing_popup_
;
1932 void AutofillDialogControllerImpl::ViewClosed() {
1933 GetManager()->RemoveObserver(this);
1935 // Called from here rather than in ~AutofillDialogControllerImpl as this
1936 // relies on virtual methods that change to their base class in the dtor.
1937 MaybeShowCreditCardBubble();
1942 std::vector
<DialogNotification
> AutofillDialogControllerImpl::
1943 CurrentNotifications() {
1944 std::vector
<DialogNotification
> notifications
;
1946 // TODO(dbeam): figure out a way to dismiss this error after a while.
1947 if (wallet_error_notification_
)
1948 notifications
.push_back(*wallet_error_notification_
);
1950 if (IsSubmitPausedOn(wallet::VERIFY_CVV
)) {
1951 notifications
.push_back(DialogNotification(
1952 DialogNotification::REQUIRED_ACTION
,
1953 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VERIFY_CVV
)));
1956 if (!wallet_server_validation_recoverable_
) {
1957 notifications
.push_back(DialogNotification(
1958 DialogNotification::REQUIRED_ACTION
,
1959 l10n_util::GetStringUTF16(
1960 IDS_AUTOFILL_DIALOG_FAILED_TO_SAVE_WALLET_DATA
)));
1963 if (choose_another_instrument_or_address_
) {
1964 notifications
.push_back(DialogNotification(
1965 DialogNotification::REQUIRED_ACTION
,
1966 l10n_util::GetStringUTF16(
1967 IDS_AUTOFILL_DIALOG_CHOOSE_DIFFERENT_WALLET_INSTRUMENT
)));
1970 if (notifications
.empty() && MenuModelForAccountChooser()) {
1971 base::string16 text
= l10n_util::GetStringUTF16(
1972 IsManuallyEditingAnySection() ?
1973 IDS_AUTOFILL_DIALOG_SAVE_DETAILS_IN_WALLET
:
1974 IDS_AUTOFILL_DIALOG_USE_WALLET
);
1975 DialogNotification
notification(
1976 DialogNotification::WALLET_USAGE_CONFIRMATION
,
1978 notification
.set_tooltip_text(
1979 l10n_util::GetStringUTF16(
1980 IDS_AUTOFILL_DIALOG_SAVE_IN_WALLET_TOOLTIP
));
1981 notification
.set_checked(IsPayingWithWallet());
1982 notifications
.push_back(notification
);
1985 if (IsPayingWithWallet() && !wallet::IsUsingProd()) {
1986 notifications
.push_back(DialogNotification(
1987 DialogNotification::DEVELOPER_WARNING
,
1988 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_NOT_PROD_WARNING
)));
1991 if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
1992 notifications
.push_back(DialogNotification(
1993 DialogNotification::SECURITY_WARNING
,
1994 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECURITY_WARNING
)));
1997 if (!invoked_from_same_origin_
) {
1998 notifications
.push_back(DialogNotification(
1999 DialogNotification::SECURITY_WARNING
,
2000 l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING
,
2001 UTF8ToUTF16(source_url_
.host()))));
2004 return notifications
;
2007 void AutofillDialogControllerImpl::LinkClicked(const GURL
& url
) {
2008 OpenTabWithUrl(url
);
2011 void AutofillDialogControllerImpl::SignInLinkClicked() {
2012 ScopedViewUpdates
updates(view_
.get());
2014 if (SignedInState() == NOT_CHECKED
) {
2015 handling_use_wallet_link_click_
= true;
2016 account_chooser_model_
.SelectActiveWalletAccount();
2017 FetchWalletCookie();
2018 view_
->UpdateAccountChooser();
2019 } else if (signin_registrar_
.IsEmpty()) {
2021 DCHECK(!IsPayingWithWallet());
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();
2029 GetMetricLogger().LogDialogUiEvent(
2030 AutofillMetrics::DIALOG_UI_SIGNIN_SHOWN
);
2032 waiting_for_explicit_sign_in_response_
= false;
2037 void AutofillDialogControllerImpl::NotificationCheckboxStateChanged(
2038 DialogNotification::Type type
, bool checked
) {
2039 if (type
== DialogNotification::WALLET_USAGE_CONFIRMATION
) {
2041 account_chooser_model_
.SelectActiveWalletAccount();
2043 account_chooser_model_
.SelectUseAutofill();
2047 void AutofillDialogControllerImpl::LegalDocumentLinkClicked(
2048 const gfx::Range
& range
) {
2049 for (size_t i
= 0; i
< legal_document_link_ranges_
.size(); ++i
) {
2050 if (legal_document_link_ranges_
[i
] == range
) {
2051 OpenTabWithUrl(wallet_items_
->legal_documents()[i
]->url());
2059 bool AutofillDialogControllerImpl::OnCancel() {
2061 if (!is_submitting_
)
2062 LogOnCancelMetrics();
2063 callback_
.Run(NULL
);
2067 bool AutofillDialogControllerImpl::OnAccept() {
2068 ScopedViewUpdates
updates(view_
.get());
2069 choose_another_instrument_or_address_
= false;
2070 wallet_server_validation_recoverable_
= true;
2073 // This must come before SetIsSubmitting().
2074 if (IsPayingWithWallet()) {
2075 // In the VERIFY_CVV case, hold onto the previously submitted cardholder
2077 if (!IsSubmitPausedOn(wallet::VERIFY_CVV
)) {
2078 submitted_cardholder_name_
=
2079 GetValueFromSection(SECTION_CC_BILLING
, NAME_BILLING_FULL
);
2081 // Snag the last four digits of the backing card now as it could be wiped
2082 // out if a CVC challenge happens.
2083 if (ActiveInstrument()) {
2084 backing_card_last_four_
= ActiveInstrument()->TypeAndLastFourDigits();
2086 DetailOutputMap output
;
2087 view_
->GetUserInput(SECTION_CC_BILLING
, &output
);
2089 GetBillingInfoFromOutputs(output
, &card
, NULL
, NULL
);
2090 backing_card_last_four_
= card
.TypeAndLastFourDigits();
2093 DCHECK(!submitted_cardholder_name_
.empty());
2094 DCHECK(!backing_card_last_four_
.empty());
2097 SetIsSubmitting(true);
2099 if (IsSubmitPausedOn(wallet::VERIFY_CVV
)) {
2100 DCHECK(!active_instrument_id_
.empty());
2101 full_wallet_
.reset();
2102 GetWalletClient()->AuthenticateInstrument(
2103 active_instrument_id_
,
2104 UTF16ToUTF8(view_
->GetCvc()));
2105 view_
->UpdateOverlay();
2106 } else if (IsPayingWithWallet()) {
2115 Profile
* AutofillDialogControllerImpl::profile() {
2119 content::WebContents
* AutofillDialogControllerImpl::GetWebContents() {
2120 return web_contents();
2123 ////////////////////////////////////////////////////////////////////////////////
2124 // AutofillPopupDelegate implementation.
2126 void AutofillDialogControllerImpl::OnPopupShown() {
2127 ScopedViewUpdates
update(view_
.get());
2128 view_
->UpdateErrorBubble();
2130 GetMetricLogger().LogDialogPopupEvent(AutofillMetrics::DIALOG_POPUP_SHOWN
);
2133 void AutofillDialogControllerImpl::OnPopupHidden() {}
2135 bool AutofillDialogControllerImpl::ShouldRepostEvent(
2136 const ui::MouseEvent
& event
) {
2137 // If the event would be reposted inside |input_showing_popup_|, just ignore.
2138 return !view_
->HitTestInput(*input_showing_popup_
, event
.location());
2141 void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier
) {
2142 // TODO(estade): implement.
2145 void AutofillDialogControllerImpl::DidAcceptSuggestion(const string16
& value
,
2147 ScopedViewUpdates
updates(view_
.get());
2148 const PersonalDataManager::GUIDPair
& pair
= popup_guids_
[identifier
];
2150 scoped_ptr
<DataModelWrapper
> wrapper
;
2151 if (common::IsCreditCardType(input_showing_popup_
->type
)) {
2152 wrapper
.reset(new AutofillCreditCardWrapper(
2153 GetManager()->GetCreditCardByGUID(pair
.first
)));
2155 wrapper
.reset(new AutofillProfileWrapper(
2156 GetManager()->GetProfileByGUID(pair
.first
),
2157 AutofillType(input_showing_popup_
->type
),
2161 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2162 DialogSection section
= static_cast<DialogSection
>(i
);
2163 wrapper
->FillInputs(MutableRequestedFieldsForSection(section
));
2164 view_
->FillSection(section
, *input_showing_popup_
);
2167 GetMetricLogger().LogDialogPopupEvent(
2168 AutofillMetrics::DIALOG_POPUP_FORM_FILLED
);
2170 // TODO(estade): not sure why it's necessary to do this explicitly.
2174 void AutofillDialogControllerImpl::RemoveSuggestion(const string16
& value
,
2176 // TODO(estade): implement.
2179 void AutofillDialogControllerImpl::ClearPreviewedForm() {
2180 // TODO(estade): implement.
2183 ////////////////////////////////////////////////////////////////////////////////
2184 // content::NotificationObserver implementation.
2186 void AutofillDialogControllerImpl::Observe(
2188 const content::NotificationSource
& source
,
2189 const content::NotificationDetails
& details
) {
2190 DCHECK_EQ(type
, content::NOTIFICATION_NAV_ENTRY_COMMITTED
);
2191 content::LoadCommittedDetails
* load_details
=
2192 content::Details
<content::LoadCommittedDetails
>(details
).ptr();
2193 if (IsSignInContinueUrl(load_details
->entry
->GetVirtualURL())) {
2194 // TODO(estade): will need to update this when we fix <crbug.com/247755>.
2195 account_chooser_model_
.SelectActiveWalletAccount();
2196 FetchWalletCookie();
2198 // NOTE: |HideSignIn()| may delete the WebContents which doesn't expect to
2199 // be deleted while committing a nav entry. Just call |HideSignIn()| later.
2200 base::MessageLoop::current()->PostTask(FROM_HERE
,
2201 base::Bind(&AutofillDialogControllerImpl::HideSignIn
,
2202 base::Unretained(this)));
2206 ////////////////////////////////////////////////////////////////////////////////
2207 // SuggestionsMenuModelDelegate implementation.
2209 void AutofillDialogControllerImpl::SuggestionsMenuWillShow() {
2213 void AutofillDialogControllerImpl::SuggestionItemSelected(
2214 SuggestionsMenuModel
* model
,
2216 ScopedViewUpdates
updates(view_
.get());
2218 if (model
->GetItemKeyAt(index
) == kManageItemsKey
) {
2220 if (!IsPayingWithWallet()) {
2221 GURL
settings_url(chrome::kChromeUISettingsURL
);
2222 url
= settings_url
.Resolve(chrome::kAutofillSubPage
);
2224 // Reset |last_wallet_items_fetch_timestamp_| to ensure that the Wallet
2225 // data is refreshed as soon as the user switches back to this tab after
2226 // potentially editing his data.
2227 last_wallet_items_fetch_timestamp_
= base::TimeTicks();
2228 size_t user_index
= account_chooser_model_
.GetActiveWalletAccountIndex();
2229 url
= SectionForSuggestionsMenuModel(*model
) == SECTION_SHIPPING
?
2230 wallet::GetManageAddressesUrl(user_index
) :
2231 wallet::GetManageInstrumentsUrl(user_index
);
2234 OpenTabWithUrl(url
);
2238 model
->SetCheckedIndex(index
);
2239 DialogSection section
= SectionForSuggestionsMenuModel(*model
);
2240 ResetSectionInput(section
);
2241 ShowEditUiIfBadSuggestion(section
);
2242 UpdateSection(section
);
2243 view_
->UpdateNotificationArea();
2246 LogSuggestionItemSelectedMetric(*model
);
2249 ////////////////////////////////////////////////////////////////////////////////
2250 // wallet::WalletClientDelegate implementation.
2252 const AutofillMetrics
& AutofillDialogControllerImpl::GetMetricLogger() const {
2253 return metric_logger_
;
2256 std::string
AutofillDialogControllerImpl::GetRiskData() const {
2257 DCHECK(!risk_data_
.empty());
2261 std::string
AutofillDialogControllerImpl::GetWalletCookieValue() const {
2262 return wallet_cookie_value_
;
2265 bool AutofillDialogControllerImpl::IsShippingAddressRequired() const {
2266 return cares_about_shipping_
;
2269 void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
2270 DCHECK(is_submitting_
&& IsPayingWithWallet());
2271 has_accepted_legal_documents_
= true;
2272 LoadRiskFingerprintData();
2275 void AutofillDialogControllerImpl::OnDidAuthenticateInstrument(bool success
) {
2276 DCHECK(is_submitting_
&& IsPayingWithWallet());
2278 // TODO(dbeam): use the returned full wallet. http://crbug.com/224992
2282 DisableWallet(wallet::WalletClient::UNKNOWN_ERROR
);
2283 SuggestionsUpdated();
2287 void AutofillDialogControllerImpl::OnDidGetFullWallet(
2288 scoped_ptr
<wallet::FullWallet
> full_wallet
) {
2289 DCHECK(is_submitting_
&& IsPayingWithWallet());
2290 ScopedViewUpdates
updates(view_
.get());
2292 full_wallet_
= full_wallet
.Pass();
2294 if (full_wallet_
->required_actions().empty()) {
2299 switch (full_wallet_
->required_actions()[0]) {
2300 case wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS
:
2301 choose_another_instrument_or_address_
= true;
2302 SetIsSubmitting(false);
2306 case wallet::VERIFY_CVV
:
2307 SuggestionsUpdated();
2311 DisableWallet(wallet::WalletClient::UNKNOWN_ERROR
);
2315 view_
->UpdateNotificationArea();
2316 view_
->UpdateButtonStrip();
2317 view_
->UpdateOverlay();
2320 void AutofillDialogControllerImpl::OnPassiveSigninSuccess() {
2321 FetchWalletCookie();
2324 void AutofillDialogControllerImpl::OnPassiveSigninFailure(
2325 const GoogleServiceAuthError
& error
) {
2326 // TODO(aruslan): report an error.
2327 LOG(ERROR
) << "failed to passively sign in: " << error
.ToString();
2328 signin_helper_
.reset();
2329 OnWalletSigninError();
2332 void AutofillDialogControllerImpl::OnDidFetchWalletCookieValue(
2333 const std::string
& cookie_value
) {
2334 wallet_cookie_value_
= cookie_value
;
2335 signin_helper_
.reset();
2339 void AutofillDialogControllerImpl::OnDidGetWalletItems(
2340 scoped_ptr
<wallet::WalletItems
> wallet_items
) {
2341 legal_documents_text_
.clear();
2342 legal_document_link_ranges_
.clear();
2343 has_accepted_legal_documents_
= false;
2345 wallet_items_
= wallet_items
.Pass();
2347 // TODO(estade): support multiple users. http://crbug.com/247755
2348 std::vector
<std::string
> username
;
2349 if (wallet_items_
&& !wallet_items_
->gaia_accounts().empty())
2350 username
.push_back(wallet_items_
->gaia_accounts()[0]);
2351 account_chooser_model_
.SetWalletAccounts(username
);
2353 ConstructLegalDocumentsText();
2354 OnWalletOrSigninUpdate();
2357 void AutofillDialogControllerImpl::OnDidSaveToWallet(
2358 const std::string
& instrument_id
,
2359 const std::string
& address_id
,
2360 const std::vector
<wallet::RequiredAction
>& required_actions
,
2361 const std::vector
<wallet::FormFieldError
>& form_field_errors
) {
2362 DCHECK(is_submitting_
&& IsPayingWithWallet());
2364 if (required_actions
.empty()) {
2365 if (!address_id
.empty())
2366 active_address_id_
= address_id
;
2367 if (!instrument_id
.empty())
2368 active_instrument_id_
= instrument_id
;
2371 OnWalletFormFieldError(form_field_errors
);
2372 HandleSaveOrUpdateRequiredActions(required_actions
);
2376 void AutofillDialogControllerImpl::OnWalletError(
2377 wallet::WalletClient::ErrorType error_type
) {
2378 DisableWallet(error_type
);
2381 ////////////////////////////////////////////////////////////////////////////////
2382 // PersonalDataManagerObserver implementation.
2384 void AutofillDialogControllerImpl::OnPersonalDataChanged() {
2388 SuggestionsUpdated();
2391 ////////////////////////////////////////////////////////////////////////////////
2392 // AccountChooserModelDelegate implementation.
2394 void AutofillDialogControllerImpl::AccountChooserWillShow() {
2398 void AutofillDialogControllerImpl::AccountChoiceChanged() {
2399 ScopedViewUpdates
updates(view_
.get());
2400 wallet::WalletClient
* client
= GetWalletClient();
2403 client
->CancelRequests();
2405 SetIsSubmitting(false);
2407 size_t selected_user_index
=
2408 account_chooser_model_
.GetActiveWalletAccountIndex();
2409 if (account_chooser_model_
.WalletIsSelected() &&
2410 client
->user_index() != selected_user_index
) {
2411 client
->CancelRequests();
2412 client
->set_user_index(selected_user_index
);
2415 SuggestionsUpdated();
2416 UpdateAccountChooserView();
2420 void AutofillDialogControllerImpl::UpdateAccountChooserView() {
2422 ScopedViewUpdates
updates(view_
.get());
2423 view_
->UpdateAccountChooser();
2424 view_
->UpdateNotificationArea();
2428 ////////////////////////////////////////////////////////////////////////////////
2430 bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
2431 const content::NativeWebKeyboardEvent
& event
) {
2432 if (popup_controller_
.get())
2433 return popup_controller_
->HandleKeyPressEvent(event
);
2438 bool AutofillDialogControllerImpl::RequestingCreditCardInfo() const {
2439 DCHECK_GT(form_structure_
.field_count(), 0U);
2441 for (size_t i
= 0; i
< form_structure_
.field_count(); ++i
) {
2442 AutofillType type
= form_structure_
.field(i
)->Type();
2443 if (common::IsCreditCardType(type
.GetStorableType()))
2450 bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
2451 return source_url_
.SchemeIs(content::kHttpsScheme
);
2454 bool AutofillDialogControllerImpl::IsSubmitPausedOn(
2455 wallet::RequiredAction required_action
) const {
2456 return full_wallet_
&& full_wallet_
->HasRequiredAction(required_action
);
2459 void AutofillDialogControllerImpl::ShowNewCreditCardBubble(
2460 scoped_ptr
<CreditCard
> new_card
,
2461 scoped_ptr
<AutofillProfile
> billing_profile
) {
2462 #if !defined(OS_ANDROID)
2463 NewCreditCardBubbleController::Show(web_contents(),
2465 billing_profile
.Pass());
2469 void AutofillDialogControllerImpl::SubmitButtonDelayBegin() {
2470 submit_button_delay_timer_
.Start(
2472 base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs
),
2474 &AutofillDialogControllerImpl::OnSubmitButtonDelayEnd
);
2477 void AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting() {
2478 DCHECK(submit_button_delay_timer_
.IsRunning());
2479 submit_button_delay_timer_
.user_task().Run();
2480 submit_button_delay_timer_
.Stop();
2483 void AutofillDialogControllerImpl::
2484 ClearLastWalletItemsFetchTimestampForTesting() {
2485 last_wallet_items_fetch_timestamp_
= base::TimeTicks();
2488 const AccountChooserModel
& AutofillDialogControllerImpl::
2489 AccountChooserModelForTesting() const {
2490 return account_chooser_model_
;
2493 bool AutofillDialogControllerImpl::IsSignInContinueUrl(const GURL
& url
) const {
2494 return wallet::IsSignInContinueUrl(url
);
2497 AutofillDialogControllerImpl::AutofillDialogControllerImpl(
2498 content::WebContents
* contents
,
2499 const FormData
& form_structure
,
2500 const GURL
& source_url
,
2501 const base::Callback
<void(const FormStructure
*)>& callback
)
2502 : WebContentsObserver(contents
),
2503 profile_(Profile::FromBrowserContext(contents
->GetBrowserContext())),
2504 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN
),
2505 form_structure_(form_structure
),
2506 invoked_from_same_origin_(true),
2507 source_url_(source_url
),
2508 callback_(callback
),
2509 account_chooser_model_(this, profile_
, metric_logger_
),
2510 wallet_client_(profile_
->GetRequestContext(), this, source_url
),
2511 wallet_items_requested_(false),
2512 handling_use_wallet_link_click_(false),
2513 country_combobox_model_(*GetManager()),
2514 suggested_cc_(this),
2515 suggested_billing_(this),
2516 suggested_cc_billing_(this),
2517 suggested_shipping_(this),
2518 cares_about_shipping_(true),
2519 input_showing_popup_(NULL
),
2520 weak_ptr_factory_(this),
2521 waiting_for_explicit_sign_in_response_(false),
2522 has_accepted_legal_documents_(false),
2523 is_submitting_(false),
2524 choose_another_instrument_or_address_(false),
2525 wallet_server_validation_recoverable_(true),
2526 data_was_passed_back_(false),
2527 was_ui_latency_logged_(false),
2528 card_generated_animation_(2000, 60, this) {
2529 // TODO(estade): remove duplicates from |form_structure|?
2530 DCHECK(!callback_
.is_null());
2533 AutofillDialogView
* AutofillDialogControllerImpl::CreateView() {
2534 return AutofillDialogView::Create(this);
2537 PersonalDataManager
* AutofillDialogControllerImpl::GetManager() {
2538 return PersonalDataManagerFactory::GetForProfile(profile_
);
2541 wallet::WalletClient
* AutofillDialogControllerImpl::GetWalletClient() {
2542 return &wallet_client_
;
2545 bool AutofillDialogControllerImpl::IsPayingWithWallet() const {
2546 return account_chooser_model_
.WalletIsSelected() &&
2547 SignedInState() == SIGNED_IN
;
2550 void AutofillDialogControllerImpl::LoadRiskFingerprintData() {
2553 uint64 obfuscated_gaia_id
= 0;
2554 bool success
= base::StringToUint64(wallet_items_
->obfuscated_gaia_id(),
2555 &obfuscated_gaia_id
);
2558 gfx::Rect window_bounds
;
2559 window_bounds
= GetBaseWindowForWebContents(web_contents())->GetBounds();
2561 PrefService
* user_prefs
= profile_
->GetPrefs();
2562 std::string charset
= user_prefs
->GetString(::prefs::kDefaultCharset
);
2563 std::string accept_languages
=
2564 user_prefs
->GetString(::prefs::kAcceptLanguages
);
2565 base::Time install_time
= base::Time::FromTimeT(
2566 g_browser_process
->local_state()->GetInt64(::prefs::kInstallDate
));
2568 risk::GetFingerprint(
2569 obfuscated_gaia_id
, window_bounds
, *web_contents(),
2570 chrome::VersionInfo().Version(), charset
, accept_languages
, install_time
,
2571 g_browser_process
->GetApplicationLocale(),
2572 base::Bind(&AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData
,
2573 weak_ptr_factory_
.GetWeakPtr()));
2576 void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
2577 scoped_ptr
<risk::Fingerprint
> fingerprint
) {
2578 DCHECK(AreLegalDocumentsCurrent());
2580 std::string proto_data
;
2581 fingerprint
->SerializeToString(&proto_data
);
2582 bool success
= base::Base64Encode(proto_data
, &risk_data_
);
2588 void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL
& url
) {
2589 chrome::NavigateParams
params(
2590 chrome::FindBrowserWithWebContents(web_contents()),
2592 content::PAGE_TRANSITION_LINK
);
2593 params
.disposition
= NEW_FOREGROUND_TAB
;
2594 chrome::Navigate(¶ms
);
2597 bool AutofillDialogControllerImpl::IsEditingExistingData(
2598 DialogSection section
) const {
2599 return section_editing_state_
.count(section
) > 0;
2602 bool AutofillDialogControllerImpl::IsManuallyEditingSection(
2603 DialogSection section
) const {
2604 return IsEditingExistingData(section
) ||
2605 SuggestionsMenuModelForSection(section
)->
2606 GetItemKeyForCheckedItem() == kAddNewItemKey
;
2609 void AutofillDialogControllerImpl::OnWalletSigninError() {
2610 account_chooser_model_
.SetHadWalletSigninError();
2611 GetWalletClient()->CancelRequests();
2612 LogDialogLatencyToShow();
2615 void AutofillDialogControllerImpl::DisableWallet(
2616 wallet::WalletClient::ErrorType error_type
) {
2617 signin_helper_
.reset();
2618 wallet_items_
.reset();
2619 wallet_errors_
.clear();
2620 GetWalletClient()->CancelRequests();
2621 SetIsSubmitting(false);
2622 wallet_error_notification_
= GetWalletError(error_type
);
2623 account_chooser_model_
.SetHadWalletError();
2626 void AutofillDialogControllerImpl::SuggestionsUpdated() {
2627 ScopedViewUpdates
updates(view_
.get());
2629 const DetailOutputMap snapshot
= TakeUserInputSnapshot();
2631 suggested_cc_
.Reset();
2632 suggested_billing_
.Reset();
2633 suggested_cc_billing_
.Reset();
2634 suggested_shipping_
.Reset();
2637 suggested_shipping_
.AddKeyedItem(
2639 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING
));
2641 if (IsPayingWithWallet()) {
2642 const std::vector
<wallet::Address
*>& addresses
=
2643 wallet_items_
->addresses();
2644 for (size_t i
= 0; i
< addresses
.size(); ++i
) {
2645 std::string key
= base::IntToString(i
);
2646 suggested_shipping_
.AddKeyedItemWithMinorText(
2648 addresses
[i
]->DisplayName(),
2649 addresses
[i
]->DisplayNameDetail());
2651 const std::string default_shipping_address_id
=
2652 GetIdToSelect(wallet_items_
->default_address_id(),
2653 previous_default_shipping_address_id_
,
2654 previously_selected_shipping_address_id_
);
2655 if (addresses
[i
]->object_id() == default_shipping_address_id
)
2656 suggested_shipping_
.SetCheckedItem(key
);
2659 if (!IsSubmitPausedOn(wallet::VERIFY_CVV
)) {
2660 const std::vector
<wallet::WalletItems::MaskedInstrument
*>& instruments
=
2661 wallet_items_
->instruments();
2662 std::string first_active_instrument_key
;
2663 std::string default_instrument_key
;
2664 for (size_t i
= 0; i
< instruments
.size(); ++i
) {
2665 bool allowed
= IsInstrumentAllowed(*instruments
[i
]);
2666 gfx::Image icon
= instruments
[i
]->CardIcon();
2667 if (!allowed
&& !icon
.IsEmpty()) {
2668 // Create a grayed disabled icon.
2669 SkBitmap disabled_bitmap
= SkBitmapOperations::CreateHSLShiftedBitmap(
2670 *icon
.ToSkBitmap(), kGrayImageShift
);
2672 gfx::ImageSkia::CreateFrom1xBitmap(disabled_bitmap
));
2674 std::string key
= base::IntToString(i
);
2675 suggested_cc_billing_
.AddKeyedItemWithMinorTextAndIcon(
2677 instruments
[i
]->DisplayName(),
2678 instruments
[i
]->DisplayNameDetail(),
2680 suggested_cc_billing_
.SetEnabled(key
, allowed
);
2683 if (first_active_instrument_key
.empty())
2684 first_active_instrument_key
= key
;
2686 const std::string default_instrument_id
=
2687 GetIdToSelect(wallet_items_
->default_instrument_id(),
2688 previous_default_instrument_id_
,
2689 previously_selected_instrument_id_
);
2690 if (instruments
[i
]->object_id() == default_instrument_id
)
2691 default_instrument_key
= key
;
2695 suggested_cc_billing_
.AddKeyedItem(
2697 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_DETAILS
));
2698 if (!wallet_items_
->HasRequiredAction(wallet::SETUP_WALLET
)) {
2699 suggested_cc_billing_
.AddKeyedItemWithMinorText(
2701 l10n_util::GetStringUTF16(
2702 IDS_AUTOFILL_DIALOG_MANAGE_BILLING_DETAILS
),
2703 UTF8ToUTF16(wallet::GetManageInstrumentsUrl(0U).host()));
2706 // Determine which instrument item should be selected.
2707 if (!default_instrument_key
.empty())
2708 suggested_cc_billing_
.SetCheckedItem(default_instrument_key
);
2709 else if (!first_active_instrument_key
.empty())
2710 suggested_cc_billing_
.SetCheckedItem(first_active_instrument_key
);
2712 suggested_cc_billing_
.SetCheckedItem(kAddNewItemKey
);
2715 PersonalDataManager
* manager
= GetManager();
2716 const std::vector
<CreditCard
*>& cards
= manager
->GetCreditCards();
2717 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
2718 for (size_t i
= 0; i
< cards
.size(); ++i
) {
2719 if (!HasCompleteAndVerifiedData(*cards
[i
], requested_cc_fields_
))
2722 suggested_cc_
.AddKeyedItemWithIcon(
2725 rb
.GetImageNamed(CreditCard::IconResourceId(cards
[i
]->type())));
2728 const std::vector
<AutofillProfile
*>& profiles
= manager
->GetProfiles();
2729 const std::string app_locale
= g_browser_process
->GetApplicationLocale();
2730 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
2731 const AutofillProfile
& profile
= *profiles
[i
];
2732 if (!HasCompleteAndVerifiedData(profile
, requested_shipping_fields_
) ||
2733 HasInvalidAddress(*profiles
[i
])) {
2737 // Don't add variants for addresses: name is part of credit card and we'll
2738 // just ignore email and phone number variants.
2739 suggested_shipping_
.AddKeyedItem(profile
.guid(), profile
.Label());
2740 if (!profile
.GetRawInfo(EMAIL_ADDRESS
).empty() &&
2741 !profile
.IsPresentButInvalid(EMAIL_ADDRESS
)) {
2742 suggested_billing_
.AddKeyedItem(profile
.guid(), profile
.Label());
2746 suggested_cc_
.AddKeyedItem(
2748 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD
));
2749 suggested_cc_
.AddKeyedItem(
2751 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD
));
2752 suggested_billing_
.AddKeyedItem(
2754 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS
));
2755 suggested_billing_
.AddKeyedItem(
2757 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS
));
2760 suggested_shipping_
.AddKeyedItem(
2762 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS
));
2763 if (!IsPayingWithWallet()) {
2764 suggested_shipping_
.AddKeyedItem(
2766 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS
));
2767 } else if (!wallet_items_
->HasRequiredAction(wallet::SETUP_WALLET
)) {
2768 suggested_shipping_
.AddKeyedItemWithMinorText(
2770 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS
),
2771 UTF8ToUTF16(wallet::GetManageAddressesUrl(0U).host()));
2774 if (!IsPayingWithWallet()) {
2775 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2776 DialogSection section
= static_cast<DialogSection
>(i
);
2777 if (!SectionIsActive(section
))
2780 // Set the starting choice for the menu. First set to the default in case
2781 // the GUID saved in prefs refers to a profile that no longer exists.
2783 GetDefaultAutofillChoice(section
, &guid
);
2784 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
2785 model
->SetCheckedItem(guid
);
2786 if (GetAutofillChoice(section
, &guid
))
2787 model
->SetCheckedItem(guid
);
2792 view_
->ModelChanged();
2794 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2795 ResetSectionInput(static_cast<DialogSection
>(i
));
2798 RestoreUserInputFromSnapshot(snapshot
);
2800 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2801 DialogSection section
= static_cast<DialogSection
>(i
);
2802 if (!SectionIsActive(section
))
2805 ShowEditUiIfBadSuggestion(section
);
2806 UpdateSection(section
);
2812 void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
2813 DialogSection section
,
2814 const InputFieldComparator
& compare
) {
2815 const DetailInputs
& inputs
= RequestedFieldsForSection(section
);
2817 if (!SectionIsActive(section
))
2820 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
2822 // Only fill in data that is associated with this section.
2823 const DetailInputs
& inputs
= RequestedFieldsForSection(section
);
2824 wrapper
->FillFormStructure(inputs
, compare
, &form_structure_
);
2826 // CVC needs special-casing because the CreditCard class doesn't store or
2827 // handle them. This isn't necessary when filling the combined CC and
2828 // billing section as CVC comes from |full_wallet_| in this case.
2829 if (section
== SECTION_CC
)
2830 SetOutputForFieldsOfType(CREDIT_CARD_VERIFICATION_CODE
, view_
->GetCvc());
2832 // When filling from Wallet data, use the email address associated with the
2833 // account. There is no other email address stored as part of a Wallet
2835 if (section
== SECTION_CC_BILLING
) {
2836 SetOutputForFieldsOfType(
2837 EMAIL_ADDRESS
, account_chooser_model_
.GetActiveWalletAccountName());
2840 // The user manually input data. If using Autofill, save the info as new or
2841 // edited data. Always fill local data into |form_structure_|.
2842 DetailOutputMap output
;
2843 view_
->GetUserInput(section
, &output
);
2845 if (section
== SECTION_CC
) {
2847 card
.set_origin(kAutofillDialogOrigin
);
2848 FillFormGroupFromOutputs(output
, &card
);
2850 // The card holder name comes from the billing address section.
2851 card
.SetRawInfo(CREDIT_CARD_NAME
,
2852 GetValueFromSection(SECTION_BILLING
, NAME_BILLING_FULL
));
2854 if (ShouldSaveDetailsLocally()) {
2855 std::string guid
= GetManager()->SaveImportedCreditCard(card
);
2856 newly_saved_data_model_guids_
[section
] = guid
;
2857 DCHECK(!profile()->IsOffTheRecord());
2858 newly_saved_card_
.reset(new CreditCard(card
));
2861 AutofillCreditCardWrapper
card_wrapper(&card
);
2862 card_wrapper
.FillFormStructure(inputs
, compare
, &form_structure_
);
2864 // Again, CVC needs special-casing. Fill it in directly from |output|.
2865 SetOutputForFieldsOfType(
2866 CREDIT_CARD_VERIFICATION_CODE
,
2867 GetValueForType(output
, CREDIT_CARD_VERIFICATION_CODE
));
2869 AutofillProfile profile
;
2870 profile
.set_origin(kAutofillDialogOrigin
);
2871 FillFormGroupFromOutputs(output
, &profile
);
2873 if (ShouldSaveDetailsLocally()) {
2874 std::string guid
= GetManager()->SaveImportedProfile(profile
);
2875 newly_saved_data_model_guids_
[section
] = guid
;
2878 AutofillProfileWrapper
profile_wrapper(&profile
);
2879 profile_wrapper
.FillFormStructure(inputs
, compare
, &form_structure_
);
2884 void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section
) {
2885 FillOutputForSectionWithComparator(
2886 section
, base::Bind(common::DetailInputMatchesField
, section
));
2889 bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
2890 DialogSection section
) const {
2891 // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
2892 // any of the fields.
2893 if (section
== SECTION_SHIPPING
)
2894 return cares_about_shipping_
;
2899 void AutofillDialogControllerImpl::SetOutputForFieldsOfType(
2900 ServerFieldType type
,
2901 const base::string16
& output
) {
2902 for (size_t i
= 0; i
< form_structure_
.field_count(); ++i
) {
2903 AutofillField
* field
= form_structure_
.field(i
);
2904 if (field
->Type().GetStorableType() == type
)
2905 field
->value
= output
;
2909 string16
AutofillDialogControllerImpl::GetValueFromSection(
2910 DialogSection section
,
2911 ServerFieldType type
) {
2912 DCHECK(SectionIsActive(section
));
2914 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
2916 return wrapper
->GetInfo(AutofillType(type
));
2918 DetailOutputMap output
;
2919 view_
->GetUserInput(section
, &output
);
2920 return GetValueForType(output
, type
);
2923 SuggestionsMenuModel
* AutofillDialogControllerImpl::
2924 SuggestionsMenuModelForSection(DialogSection section
) {
2927 return &suggested_cc_
;
2928 case SECTION_BILLING
:
2929 return &suggested_billing_
;
2930 case SECTION_SHIPPING
:
2931 return &suggested_shipping_
;
2932 case SECTION_CC_BILLING
:
2933 return &suggested_cc_billing_
;
2940 const SuggestionsMenuModel
* AutofillDialogControllerImpl::
2941 SuggestionsMenuModelForSection(DialogSection section
) const {
2942 return const_cast<AutofillDialogControllerImpl
*>(this)->
2943 SuggestionsMenuModelForSection(section
);
2946 DialogSection
AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
2947 const SuggestionsMenuModel
& model
) {
2948 if (&model
== &suggested_cc_
)
2951 if (&model
== &suggested_billing_
)
2952 return SECTION_BILLING
;
2954 if (&model
== &suggested_cc_billing_
)
2955 return SECTION_CC_BILLING
;
2957 DCHECK_EQ(&model
, &suggested_shipping_
);
2958 return SECTION_SHIPPING
;
2961 DetailInputs
* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
2962 DialogSection section
) {
2963 return const_cast<DetailInputs
*>(&RequestedFieldsForSection(section
));
2966 void AutofillDialogControllerImpl::HidePopup() {
2967 if (popup_controller_
.get())
2968 popup_controller_
->Hide();
2969 input_showing_popup_
= NULL
;
2972 void AutofillDialogControllerImpl::SetEditingExistingData(
2973 DialogSection section
, bool editing
) {
2975 section_editing_state_
.insert(section
);
2977 section_editing_state_
.erase(section
);
2980 bool AutofillDialogControllerImpl::IsASuggestionItemKey(
2981 const std::string
& key
) const {
2982 return !key
.empty() &&
2983 key
!= kAddNewItemKey
&&
2984 key
!= kManageItemsKey
&&
2985 key
!= kSameAsBillingKey
;
2988 bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
2989 for (size_t section
= SECTION_MIN
; section
<= SECTION_MAX
; ++section
) {
2990 if (IsManuallyEditingSection(static_cast<DialogSection
>(section
)))
2996 base::string16
AutofillDialogControllerImpl::CreditCardNumberValidityMessage(
2997 const base::string16
& number
) const {
2998 if (!number
.empty() && !autofill::IsValidCreditCardNumber(number
)) {
2999 return l10n_util::GetStringUTF16(
3000 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER
);
3003 base::string16 message
;
3004 if (IsPayingWithWallet() && !wallet_items_
->SupportsCard(number
, &message
))
3007 // Card number is good and supported.
3008 return base::string16();
3011 bool AutofillDialogControllerImpl::AllSectionsAreValid() {
3012 for (size_t section
= SECTION_MIN
; section
<= SECTION_MAX
; ++section
) {
3013 if (!SectionIsValid(static_cast<DialogSection
>(section
)))
3019 bool AutofillDialogControllerImpl::SectionIsValid(
3020 DialogSection section
) {
3021 if (!IsManuallyEditingSection(section
))
3024 DetailOutputMap detail_outputs
;
3025 view_
->GetUserInput(section
, &detail_outputs
);
3026 return !InputsAreValid(section
, detail_outputs
).HasSureErrors();
3029 bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
3030 const base::string16
& year
,
3031 const base::string16
& month
) const {
3032 // If the expiration is in the past as per the local clock, it's invalid.
3033 base::Time now
= base::Time::Now();
3034 if (!autofill::IsValidCreditCardExpirationDate(year
, month
, now
))
3037 if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING
)) {
3038 const wallet::WalletItems::MaskedInstrument
* instrument
=
3040 const std::string
& locale
= g_browser_process
->GetApplicationLocale();
3042 if (base::StringToInt(month
, &month_int
) &&
3043 instrument
->status() ==
3044 wallet::WalletItems::MaskedInstrument::EXPIRED
&&
3046 instrument
->GetInfo(
3047 AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR
), locale
) &&
3048 month_int
== instrument
->expiration_month()) {
3049 // Otherwise, if the user is editing an instrument that's deemed expired
3050 // by the Online Wallet server, mark it invalid on selection.
3058 bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
3059 return SectionIsActive(SECTION_SHIPPING
) &&
3060 suggested_shipping_
.GetItemKeyForCheckedItem() == kSameAsBillingKey
;
3063 bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
3064 // It's possible that the user checked [X] Save details locally before
3065 // switching payment methods, so only ask the view whether to save details
3066 // locally if that checkbox is showing (currently if not paying with wallet).
3067 // Also, if the user isn't editing any sections, there's no data to save
3069 return ShouldOfferToSaveInChrome() && view_
->SaveDetailsLocally();
3072 void AutofillDialogControllerImpl::SetIsSubmitting(bool submitting
) {
3073 is_submitting_
= submitting
;
3076 full_wallet_
.reset();
3079 ScopedViewUpdates
updates(view_
.get());
3080 view_
->UpdateButtonStrip();
3081 view_
->UpdateOverlay();
3082 view_
->UpdateNotificationArea();
3086 bool AutofillDialogControllerImpl::AreLegalDocumentsCurrent() const {
3087 return has_accepted_legal_documents_
||
3088 (wallet_items_
&& wallet_items_
->legal_documents().empty());
3091 void AutofillDialogControllerImpl::AcceptLegalTerms() {
3092 content::BrowserThread::PostTask(
3093 content::BrowserThread::IO
, FROM_HERE
,
3094 base::Bind(&UserDidOptIntoLocationServices
));
3095 PrefService
* local_state
= g_browser_process
->local_state();
3096 ListPrefUpdate
accepted(
3097 local_state
, ::prefs::kAutofillDialogWalletLocationAcceptance
);
3098 accepted
->AppendIfNotPresent(new base::StringValue(
3099 account_chooser_model_
.GetActiveWalletAccountName()));
3101 if (AreLegalDocumentsCurrent()) {
3102 LoadRiskFingerprintData();
3104 GetWalletClient()->AcceptLegalDocuments(
3105 wallet_items_
->legal_documents(),
3106 wallet_items_
->google_transaction_id());
3110 void AutofillDialogControllerImpl::SubmitWithWallet() {
3111 active_instrument_id_
.clear();
3112 active_address_id_
.clear();
3113 full_wallet_
.reset();
3115 const wallet::WalletItems::MaskedInstrument
* active_instrument
=
3117 if (!IsManuallyEditingSection(SECTION_CC_BILLING
)) {
3118 active_instrument_id_
= active_instrument
->object_id();
3119 DCHECK(!active_instrument_id_
.empty());
3122 const wallet::Address
* active_address
= ActiveShippingAddress();
3123 if (!IsManuallyEditingSection(SECTION_SHIPPING
) &&
3124 !ShouldUseBillingForShipping() &&
3125 IsShippingAddressRequired()) {
3126 active_address_id_
= active_address
->object_id();
3127 DCHECK(!active_address_id_
.empty());
3130 scoped_ptr
<wallet::Instrument
> inputted_instrument
=
3131 CreateTransientInstrument();
3132 if (inputted_instrument
&& IsEditingExistingData(SECTION_CC_BILLING
)) {
3133 inputted_instrument
->set_object_id(active_instrument
->object_id());
3134 DCHECK(!inputted_instrument
->object_id().empty());
3137 scoped_ptr
<wallet::Address
> inputted_address
;
3138 if (active_address_id_
.empty() && IsShippingAddressRequired()) {
3139 if (ShouldUseBillingForShipping()) {
3140 const wallet::Address
& address
= inputted_instrument
?
3141 *inputted_instrument
->address() : active_instrument
->address();
3142 // Try to find an exact matched shipping address and use it for shipping,
3143 // otherwise save it as a new shipping address. http://crbug.com/225442
3144 const wallet::Address
* duplicated_address
=
3145 FindDuplicateAddress(wallet_items_
->addresses(), address
);
3146 if (duplicated_address
) {
3147 active_address_id_
= duplicated_address
->object_id();
3148 DCHECK(!active_address_id_
.empty());
3150 inputted_address
.reset(new wallet::Address(address
));
3151 DCHECK(inputted_address
->object_id().empty());
3154 inputted_address
= CreateTransientAddress();
3155 if (IsEditingExistingData(SECTION_SHIPPING
)) {
3156 inputted_address
->set_object_id(active_address
->object_id());
3157 DCHECK(!inputted_address
->object_id().empty());
3162 // If there's neither an address nor instrument to save, |GetFullWallet()|
3163 // is called when the risk fingerprint is loaded.
3164 if (!active_instrument_id_
.empty() &&
3165 (!active_address_id_
.empty() || !IsShippingAddressRequired())) {
3170 GetWalletClient()->SaveToWallet(inputted_instrument
.Pass(),
3171 inputted_address
.Pass());
3174 scoped_ptr
<wallet::Instrument
> AutofillDialogControllerImpl::
3175 CreateTransientInstrument() {
3176 if (!active_instrument_id_
.empty())
3177 return scoped_ptr
<wallet::Instrument
>();
3179 DetailOutputMap output
;
3180 view_
->GetUserInput(SECTION_CC_BILLING
, &output
);
3183 AutofillProfile profile
;
3185 GetBillingInfoFromOutputs(output
, &card
, &cvc
, &profile
);
3187 return scoped_ptr
<wallet::Instrument
>(
3188 new wallet::Instrument(card
, cvc
, profile
));
3191 scoped_ptr
<wallet::Address
>AutofillDialogControllerImpl::
3192 CreateTransientAddress() {
3193 // If not using billing for shipping, just scrape the view.
3194 DetailOutputMap output
;
3195 view_
->GetUserInput(SECTION_SHIPPING
, &output
);
3197 AutofillProfile profile
;
3198 FillFormGroupFromOutputs(output
, &profile
);
3200 return scoped_ptr
<wallet::Address
>(new wallet::Address(profile
));
3203 void AutofillDialogControllerImpl::GetFullWallet() {
3204 DCHECK(is_submitting_
);
3205 DCHECK(IsPayingWithWallet());
3206 DCHECK(wallet_items_
);
3207 DCHECK(!active_instrument_id_
.empty());
3208 DCHECK(!active_address_id_
.empty() || !IsShippingAddressRequired());
3210 std::vector
<wallet::WalletClient::RiskCapability
> capabilities
;
3211 capabilities
.push_back(wallet::WalletClient::VERIFY_CVC
);
3213 GetWalletClient()->GetFullWallet(wallet::WalletClient::FullWalletRequest(
3214 active_instrument_id_
,
3216 wallet_items_
->google_transaction_id(),
3218 wallet_items_
->HasRequiredAction(wallet::SETUP_WALLET
)));
3221 void AutofillDialogControllerImpl::HandleSaveOrUpdateRequiredActions(
3222 const std::vector
<wallet::RequiredAction
>& required_actions
) {
3223 DCHECK(!required_actions
.empty());
3225 // TODO(ahutter): Investigate if we need to support more generic actions on
3226 // this call such as GAIA_AUTH. See crbug.com/243457.
3227 for (std::vector
<wallet::RequiredAction
>::const_iterator iter
=
3228 required_actions
.begin();
3229 iter
!= required_actions
.end(); ++iter
) {
3230 if (*iter
!= wallet::INVALID_FORM_FIELD
) {
3231 // TODO(dbeam): handle this more gracefully.
3232 DisableWallet(wallet::WalletClient::UNKNOWN_ERROR
);
3235 SetIsSubmitting(false);
3238 void AutofillDialogControllerImpl::FinishSubmit() {
3239 if (IsPayingWithWallet()) {
3240 ScopedViewUpdates
updates(view_
.get());
3241 view_
->UpdateOverlay();
3243 card_generated_animation_
.Start();
3249 void AutofillDialogControllerImpl::AnimationProgressed(
3250 const gfx::Animation
* animation
) {
3251 DCHECK_EQ(animation
, &card_generated_animation_
);
3252 PushOverlayUpdate();
3255 void AutofillDialogControllerImpl::AnimationEnded(
3256 const gfx::Animation
* animation
) {
3257 DCHECK_EQ(animation
, &card_generated_animation_
);
3261 void AutofillDialogControllerImpl::DoFinishSubmit() {
3262 FillOutputForSection(SECTION_CC
);
3263 FillOutputForSection(SECTION_BILLING
);
3264 FillOutputForSection(SECTION_CC_BILLING
);
3266 if (ShouldUseBillingForShipping()) {
3267 FillOutputForSectionWithComparator(
3269 base::Bind(DetailInputMatchesShippingField
));
3270 FillOutputForSectionWithComparator(
3272 base::Bind(DetailInputMatchesShippingField
));
3273 FillOutputForSectionWithComparator(
3275 base::Bind(DetailInputMatchesShippingField
));
3277 FillOutputForSection(SECTION_SHIPPING
);
3280 if (!IsPayingWithWallet()) {
3281 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
3282 DialogSection section
= static_cast<DialogSection
>(i
);
3283 if (!SectionIsActive(section
))
3286 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
3287 std::string item_key
= model
->GetItemKeyForCheckedItem();
3288 if (IsASuggestionItemKey(item_key
) || item_key
== kSameAsBillingKey
) {
3289 PersistAutofillChoice(section
, item_key
);
3290 } else if (item_key
== kAddNewItemKey
&& ShouldSaveDetailsLocally()) {
3291 DCHECK(newly_saved_data_model_guids_
.count(section
));
3292 PersistAutofillChoice(section
, newly_saved_data_model_guids_
[section
]);
3296 profile_
->GetPrefs()->SetBoolean(::prefs::kAutofillDialogSaveData
,
3297 view_
->SaveDetailsLocally());
3300 // On a successful submit, if the user manually selected "pay without wallet",
3301 // stop trying to pay with Wallet on future runs of the dialog. On the other
3302 // hand, if there was an error that prevented the user from having the choice
3303 // of using Wallet, leave the pref alone.
3304 if (!wallet_error_notification_
&&
3305 account_chooser_model_
.HasAccountsToChoose()) {
3306 profile_
->GetPrefs()->SetBoolean(
3307 ::prefs::kAutofillDialogPayWithoutWallet
,
3308 !account_chooser_model_
.WalletIsSelected());
3311 LogOnFinishSubmitMetrics();
3313 // Callback should be called as late as possible.
3314 callback_
.Run(&form_structure_
);
3315 data_was_passed_back_
= true;
3317 // This might delete us.
3321 void AutofillDialogControllerImpl::PersistAutofillChoice(
3322 DialogSection section
,
3323 const std::string
& guid
) {
3324 DCHECK(!IsPayingWithWallet());
3325 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
3326 value
->SetString(kGuidPrefKey
, guid
);
3328 DictionaryPrefUpdate
updater(profile()->GetPrefs(),
3329 ::prefs::kAutofillDialogAutofillDefault
);
3330 base::DictionaryValue
* autofill_choice
= updater
.Get();
3331 autofill_choice
->Set(SectionToPrefString(section
), value
.release());
3334 void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
3335 DialogSection section
,
3336 std::string
* guid
) {
3337 DCHECK(!IsPayingWithWallet());
3338 // The default choice is the first thing in the menu that is a suggestion
3340 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
3341 for (int i
= 0; i
< model
->GetItemCount(); ++i
) {
3342 if (IsASuggestionItemKey(model
->GetItemKeyAt(i
))) {
3343 *guid
= model
->GetItemKeyAt(i
);
3349 bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section
,
3350 std::string
* guid
) {
3351 DCHECK(!IsPayingWithWallet());
3352 const base::DictionaryValue
* choices
= profile()->GetPrefs()->GetDictionary(
3353 ::prefs::kAutofillDialogAutofillDefault
);
3357 const base::DictionaryValue
* choice
= NULL
;
3358 if (!choices
->GetDictionary(SectionToPrefString(section
), &choice
))
3361 choice
->GetString(kGuidPrefKey
, guid
);
3365 void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
3366 GetMetricLogger().LogDialogUiDuration(
3367 base::Time::Now() - dialog_shown_timestamp_
,
3368 AutofillMetrics::DIALOG_ACCEPTED
);
3370 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED
);
3372 AutofillMetrics::DialogDismissalState dismissal_state
;
3373 if (!IsManuallyEditingAnySection())
3374 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_EXISTING_DATA
;
3375 else if (IsPayingWithWallet())
3376 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_WALLET
;
3377 else if (ShouldSaveDetailsLocally())
3378 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL
;
3380 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE
;
3382 GetMetricLogger().LogDialogDismissalState(dismissal_state
);
3385 void AutofillDialogControllerImpl::LogOnCancelMetrics() {
3386 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED
);
3388 AutofillMetrics::DialogDismissalState dismissal_state
;
3389 if (ShouldShowSignInWebView())
3390 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_DURING_SIGNIN
;
3391 else if (!IsManuallyEditingAnySection())
3392 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_NO_EDITS
;
3393 else if (AllSectionsAreValid())
3394 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS
;
3396 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS
;
3398 GetMetricLogger().LogDialogDismissalState(dismissal_state
);
3400 GetMetricLogger().LogDialogUiDuration(
3401 base::Time::Now() - dialog_shown_timestamp_
,
3402 AutofillMetrics::DIALOG_CANCELED
);
3405 void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
3406 const SuggestionsMenuModel
& model
) {
3407 DialogSection section
= SectionForSuggestionsMenuModel(model
);
3409 AutofillMetrics::DialogUiEvent dialog_ui_event
;
3410 if (model
.GetItemKeyForCheckedItem() == kAddNewItemKey
) {
3411 // Selected to add a new item.
3412 dialog_ui_event
= common::DialogSectionToUiItemAddedEvent(section
);
3413 } else if (IsASuggestionItemKey(model
.GetItemKeyForCheckedItem())) {
3414 // Selected an existing item.
3415 dialog_ui_event
= common::DialogSectionToUiSelectionChangedEvent(section
);
3417 // TODO(estade): add logging for "Manage items" or "Use billing for
3422 GetMetricLogger().LogDialogUiEvent(dialog_ui_event
);
3425 void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
3426 if (was_ui_latency_logged_
)
3429 GetMetricLogger().LogDialogLatencyToShow(
3430 base::Time::Now() - dialog_shown_timestamp_
);
3431 was_ui_latency_logged_
= true;
3434 AutofillMetrics::DialogInitialUserStateMetric
3435 AutofillDialogControllerImpl::GetInitialUserState() const {
3436 // Consider a user to be an Autofill user if the user has any credit cards
3437 // or addresses saved. Check that the item count is greater than 2 because
3438 // an "empty" menu still has the "add new" menu item and "manage" menu item.
3439 const bool has_autofill_profiles
=
3440 suggested_cc_
.GetItemCount() > 2 ||
3441 suggested_billing_
.GetItemCount() > 2;
3443 if (SignedInState() != SIGNED_IN
) {
3445 return has_autofill_profiles
?
3446 AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL
:
3447 AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL
;
3451 if (wallet_items_
->instruments().empty()) {
3453 return has_autofill_profiles
?
3454 AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL
:
3455 AutofillMetrics::DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL
;
3458 // Has Wallet items.
3459 return has_autofill_profiles
?
3460 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL
:
3461 AutofillMetrics::DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL
;
3464 void AutofillDialogControllerImpl::MaybeShowCreditCardBubble() {
3465 if (!data_was_passed_back_
)
3468 if (newly_saved_card_
) {
3469 scoped_ptr
<AutofillProfile
> billing_profile
;
3470 if (IsManuallyEditingSection(SECTION_BILLING
)) {
3471 // Scrape the view as the user's entering or updating information.
3472 DetailOutputMap outputs
;
3473 view_
->GetUserInput(SECTION_BILLING
, &outputs
);
3474 billing_profile
.reset(new AutofillProfile
);
3475 FillFormGroupFromOutputs(outputs
, billing_profile
.get());
3477 // Just snag the currently suggested profile.
3478 std::string item_key
= SuggestionsMenuModelForSection(SECTION_BILLING
)->
3479 GetItemKeyForCheckedItem();
3480 AutofillProfile
* profile
= GetManager()->GetProfileByGUID(item_key
);
3481 billing_profile
.reset(new AutofillProfile(*profile
));
3484 ShowNewCreditCardBubble(newly_saved_card_
.Pass(),
3485 billing_profile
.Pass());
3489 if (!full_wallet_
|| !full_wallet_
->billing_address())
3492 #if !defined(OS_ANDROID)
3493 GeneratedCreditCardBubbleController::Show(
3495 full_wallet_
->TypeAndLastFourDigits(),
3496 backing_card_last_four_
);
3500 void AutofillDialogControllerImpl::OnSubmitButtonDelayEnd() {
3503 ScopedViewUpdates
updates(view_
.get());
3504 view_
->UpdateButtonStrip();
3507 void AutofillDialogControllerImpl::FetchWalletCookie() {
3508 net::URLRequestContextGetter
* request_context
= profile_
->GetRequestContext();
3509 signin_helper_
.reset(new wallet::WalletSigninHelper(this, request_context
));
3510 signin_helper_
->StartWalletCookieValueFetch();
3513 } // namespace autofill