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