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