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