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