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