Home | History | Annotate | Download | only in autofill
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/android/autofill/autofill_dialog_controller_android.h"
      6 
      7 #include "base/android/jni_android.h"
      8 #include "base/android/jni_array.h"
      9 #include "base/android/jni_string.h"
     10 #include "base/android/scoped_java_ref.h"
     11 #include "base/bind.h"
     12 #include "base/logging.h"
     13 #include "base/prefs/pref_service.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chrome/browser/autofill/personal_data_manager_factory.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/profiles/profile_manager.h"
     20 #include "chrome/browser/ui/android/autofill/autofill_dialog_result.h"
     21 #include "chrome/browser/ui/android/window_android_helper.h"
     22 #include "chrome/browser/ui/autofill/autofill_dialog_common.h"
     23 #include "chrome/browser/ui/autofill/data_model_wrapper.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "chrome/common/url_constants.h"
     26 #include "components/autofill/content/browser/wallet/full_wallet.h"
     27 #include "components/autofill/core/browser/autofill_metrics.h"
     28 #include "components/autofill/core/browser/autofill_profile.h"
     29 #include "components/autofill/core/browser/autofill_type.h"
     30 #include "components/autofill/core/browser/credit_card.h"
     31 #include "components/autofill/core/browser/personal_data_manager.h"
     32 #include "components/autofill/core/common/form_data.h"
     33 #include "components/user_prefs/pref_registry_syncable.h"
     34 #include "content/public/browser/navigation_controller.h"
     35 #include "content/public/browser/navigation_details.h"
     36 #include "content/public/browser/navigation_entry.h"
     37 #include "content/public/browser/web_contents.h"
     38 #include "grit/generated_resources.h"
     39 #include "jni/AutofillDialogControllerAndroid_jni.h"
     40 #include "ui/android/window_android.h"
     41 #include "ui/base/l10n/l10n_util.h"
     42 #include "ui/base/models/combobox_model.h"
     43 #include "ui/base/models/menu_model.h"
     44 #include "ui/gfx/android/java_bitmap.h"
     45 #include "ui/gfx/rect.h"
     46 #include "url/gurl.h"
     47 
     48 namespace autofill {
     49 
     50 namespace {
     51 
     52 // Keys in kAutofillDialogDefaults pref dictionary (do not change these values).
     53 const char kLastUsedAccountName[] = "last_used_account_name";
     54 const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill";
     55 const char kLastUsedBillingAddressGuid[] = "last_used_billing";
     56 const char kLastUsedShippingAddressGuid[] = "last_used_shipping";
     57 const char kLastUsedCreditCardGuid[] = "last_used_card";
     58 
     59 scoped_ptr<DataModelWrapper> CreateWrapper(
     60     DialogSection section, wallet::FullWallet* full_wallet) {
     61   if (section == SECTION_CC_BILLING) {
     62     if (!full_wallet->billing_address())
     63       return scoped_ptr<DataModelWrapper>();
     64 
     65     return scoped_ptr<DataModelWrapper>(
     66         new FullWalletBillingWrapper(full_wallet));
     67   }
     68   if (section == SECTION_SHIPPING) {
     69     if (!full_wallet->shipping_address())
     70       return scoped_ptr<DataModelWrapper>();
     71 
     72     return scoped_ptr<DataModelWrapper>(
     73         new FullWalletShippingWrapper(full_wallet));
     74   }
     75   NOTREACHED();
     76   return scoped_ptr<DataModelWrapper>();
     77 }
     78 
     79 void FillOutputForSectionWithComparator(
     80     DialogSection section, const DetailInputs& inputs,
     81     const InputFieldComparator& compare,
     82     FormStructure& form_structure, wallet::FullWallet* full_wallet,
     83     const base::string16& email_address) {
     84 
     85   // Email is hidden while using Wallet, special case it.
     86   if (section == SECTION_EMAIL) {
     87     AutofillProfile profile;
     88     profile.SetRawInfo(EMAIL_ADDRESS, email_address);
     89     AutofillProfileWrapper profile_wrapper(&profile, 0);
     90     profile_wrapper.FillFormStructure(inputs, compare, &form_structure);
     91     return;
     92   }
     93 
     94   scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section, full_wallet);
     95   if (wrapper)
     96     wrapper->FillFormStructure(inputs, compare, &form_structure);
     97 }
     98 
     99 void FillOutputForSection(
    100     DialogSection section,
    101     FormStructure& form_structure,
    102     wallet::FullWallet* full_wallet,
    103     const base::string16& email_address) {
    104   DetailInputs inputs;
    105   common::BuildInputsForSection(section, &inputs);
    106 
    107   FillOutputForSectionWithComparator(
    108       section, inputs,
    109       base::Bind(common::DetailInputMatchesField, section),
    110       form_structure, full_wallet, email_address);
    111 }
    112 
    113 }  // namespace
    114 
    115 
    116 // static
    117 base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create(
    118     content::WebContents* contents,
    119     const FormData& form_structure,
    120     const GURL& source_url,
    121     const DialogType dialog_type,
    122     const base::Callback<void(const FormStructure*,
    123                               const std::string&)>& callback) {
    124   // AutofillDialogControllerAndroid owns itself.
    125   AutofillDialogControllerAndroid* autofill_dialog_controller =
    126       new AutofillDialogControllerAndroid(contents,
    127                                           form_structure,
    128                                           source_url,
    129                                           dialog_type,
    130                                           callback);
    131   return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
    132 }
    133 
    134 // static
    135 void AutofillDialogControllerAndroid::RegisterProfilePrefs(
    136     user_prefs::PrefRegistrySyncable* registry) {
    137   registry->RegisterDictionaryPref(
    138       ::prefs::kAutofillDialogDefaults,
    139       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
    140 }
    141 
    142 // static
    143 base::WeakPtr<AutofillDialogController>
    144 AutofillDialogController::Create(
    145     content::WebContents* contents,
    146     const FormData& form_structure,
    147     const GURL& source_url,
    148     const DialogType dialog_type,
    149     const base::Callback<void(const FormStructure*,
    150                               const std::string&)>& callback) {
    151   return AutofillDialogControllerAndroid::Create(contents,
    152                                                  form_structure,
    153                                                  source_url,
    154                                                  dialog_type,
    155                                                  callback);
    156 }
    157 
    158 // static
    159 void AutofillDialogController::RegisterProfilePrefs(
    160     user_prefs::PrefRegistrySyncable* registry) {
    161   AutofillDialogControllerAndroid::RegisterProfilePrefs(registry);
    162 }
    163 
    164 AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
    165   JNIEnv* env = base::android::AttachCurrentThread();
    166   Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
    167 }
    168 
    169 void AutofillDialogControllerAndroid::Show() {
    170   dialog_shown_timestamp_ = base::Time::Now();
    171 
    172   content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
    173   const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
    174   invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
    175 
    176   // Log any relevant UI metrics and security exceptions.
    177   GetMetricLogger().LogDialogUiEvent(
    178       GetDialogType(), AutofillMetrics::DIALOG_UI_SHOWN);
    179 
    180   GetMetricLogger().LogDialogSecurityMetric(
    181       GetDialogType(), AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
    182 
    183   if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
    184     GetMetricLogger().LogDialogSecurityMetric(
    185         GetDialogType(),
    186         AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
    187   }
    188 
    189   if (!invoked_from_same_origin_) {
    190     GetMetricLogger().LogDialogSecurityMetric(
    191         GetDialogType(),
    192         AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
    193   }
    194 
    195   // Determine what field types should be included in the dialog.
    196   bool has_types = false;
    197   bool has_sections = false;
    198   form_structure_.ParseFieldTypesFromAutocompleteAttributes(
    199       &has_types, &has_sections);
    200 
    201   // Fail if the author didn't specify autocomplete types.
    202   if (!has_types) {
    203     callback_.Run(NULL, std::string());
    204     delete this;
    205     return;
    206   }
    207 
    208   bool request_full_billing_address = true;
    209   bool request_shipping_address = false;
    210   bool request_phone_numbers = false;
    211 
    212   for (size_t i = 0; i < form_structure_.field_count(); ++i) {
    213     const ServerFieldType type =
    214         form_structure_.field(i)->Type().GetStorableType();
    215     if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER) {
    216       request_phone_numbers = true;
    217     }
    218     if (type == NAME_FULL ||
    219         type == ADDRESS_HOME_LINE1 || type == ADDRESS_HOME_LINE2 ||
    220         type == ADDRESS_HOME_CITY || type == ADDRESS_HOME_STATE ||
    221         type == ADDRESS_HOME_ZIP || type == ADDRESS_HOME_COUNTRY ||
    222         type == PHONE_HOME_WHOLE_NUMBER) {
    223       request_shipping_address = true;
    224     }
    225     if (type == ADDRESS_BILLING_LINE1 || type == ADDRESS_BILLING_LINE2 ||
    226         type == ADDRESS_BILLING_CITY || type == ADDRESS_BILLING_STATE ||
    227         type == PHONE_BILLING_WHOLE_NUMBER) {
    228       request_full_billing_address = true;
    229     }
    230   }
    231 
    232   if (request_shipping_address)
    233     request_full_billing_address = true;
    234 
    235   const bool incognito_mode = profile_->IsOffTheRecord();
    236 
    237   bool last_used_choice_is_autofill = false;
    238   base::string16 last_used_account_name;
    239   std::string last_used_billing;
    240   std::string last_used_shipping;
    241   std::string last_used_credit_card;
    242   {
    243     const base::DictionaryValue* defaults =
    244         profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
    245     if (defaults) {
    246       defaults->GetString(kLastUsedAccountName, &last_used_account_name);
    247       defaults->GetBoolean(kLastUsedChoiceIsAutofill,
    248                            &last_used_choice_is_autofill);
    249       defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
    250       defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
    251       defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
    252     } else {
    253       DLOG(ERROR) << "Failed to read AutofillDialog preferences";
    254     }
    255   }
    256 
    257   if (contents_->GetBrowserContext()->IsOffTheRecord())
    258     last_used_choice_is_autofill = true;
    259 
    260   JNIEnv* env = base::android::AttachCurrentThread();
    261   ScopedJavaLocalRef<jstring> jlast_used_account_name =
    262       base::android::ConvertUTF16ToJavaString(
    263           env, last_used_account_name);
    264   ScopedJavaLocalRef<jstring> jlast_used_billing =
    265       base::android::ConvertUTF8ToJavaString(
    266           env, last_used_billing);
    267   ScopedJavaLocalRef<jstring> jlast_used_shipping =
    268       base::android::ConvertUTF8ToJavaString(
    269           env, last_used_shipping);
    270   ScopedJavaLocalRef<jstring> jlast_used_card =
    271       base::android::ConvertUTF8ToJavaString(
    272           env, last_used_credit_card);
    273   ScopedJavaLocalRef<jstring> jmerchant_domain =
    274       base::android::ConvertUTF8ToJavaString(
    275           env, source_url_.GetOrigin().spec());
    276   java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
    277       env,
    278       reinterpret_cast<jint>(this),
    279       WindowAndroidHelper::FromWebContents(contents_)->
    280           GetWindowAndroid()->GetJavaObject().obj(),
    281       request_full_billing_address, request_shipping_address,
    282       request_phone_numbers, incognito_mode,
    283       last_used_choice_is_autofill, jlast_used_account_name.obj(),
    284       jlast_used_billing.obj(), jlast_used_shipping.obj(),
    285       jlast_used_card.obj(),
    286       jmerchant_domain.obj()));
    287 }
    288 
    289 void AutofillDialogControllerAndroid::Hide() {
    290   // TODO(aruslan): http://crbug.com/177373 Autocheckout.
    291   NOTIMPLEMENTED();
    292 }
    293 
    294 void AutofillDialogControllerAndroid::TabActivated() {}
    295 
    296 void AutofillDialogControllerAndroid::AddAutocheckoutStep(
    297     AutocheckoutStepType step_type) {
    298   // TODO(aruslan): http://crbug.com/177373 Autocheckout.
    299   NOTIMPLEMENTED() << " step_type = " << step_type;
    300 }
    301 
    302 void AutofillDialogControllerAndroid::UpdateAutocheckoutStep(
    303     AutocheckoutStepType step_type,
    304     AutocheckoutStepStatus step_status) {
    305   // TODO(aruslan): http://crbug.com/177373 Autocheckout.
    306   NOTIMPLEMENTED() << " step_type=" << step_type
    307                    << " step_status=" << step_status;
    308 }
    309 
    310 void AutofillDialogControllerAndroid::OnAutocheckoutError() {
    311   // TODO(aruslan): http://crbug.com/177373 Autocheckout.
    312   NOTIMPLEMENTED();
    313   DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
    314   GetMetricLogger().LogAutocheckoutDuration(
    315       base::Time::Now() - autocheckout_started_timestamp_,
    316       AutofillMetrics::AUTOCHECKOUT_FAILED);
    317   SetAutocheckoutState(AUTOCHECKOUT_ERROR);
    318   autocheckout_started_timestamp_ = base::Time();
    319 }
    320 
    321 void AutofillDialogControllerAndroid::OnAutocheckoutSuccess() {
    322   // TODO(aruslan): http://crbug.com/177373 Autocheckout.
    323   NOTIMPLEMENTED();
    324   DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
    325   GetMetricLogger().LogAutocheckoutDuration(
    326       base::Time::Now() - autocheckout_started_timestamp_,
    327       AutofillMetrics::AUTOCHECKOUT_SUCCEEDED);
    328   SetAutocheckoutState(AUTOCHECKOUT_SUCCESS);
    329   autocheckout_started_timestamp_ = base::Time();
    330 }
    331 
    332 DialogType AutofillDialogControllerAndroid::GetDialogType() const {
    333   return dialog_type_;
    334 }
    335 
    336 // static
    337 bool AutofillDialogControllerAndroid::
    338     RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
    339   return RegisterNativesImpl(env);
    340 }
    341 
    342 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
    343                                                    jobject obj) {
    344   if (autocheckout_state_ == AUTOCHECKOUT_NOT_STARTED)
    345     LogOnCancelMetrics();
    346 
    347   if (autocheckout_state_ == AUTOCHECKOUT_IN_PROGRESS) {
    348     GetMetricLogger().LogAutocheckoutDuration(
    349         base::Time::Now() - autocheckout_started_timestamp_,
    350         AutofillMetrics::AUTOCHECKOUT_CANCELLED);
    351   }
    352 
    353   callback_.Run(NULL, std::string());
    354 }
    355 
    356 void AutofillDialogControllerAndroid::DialogContinue(
    357     JNIEnv* env,
    358     jobject obj,
    359     jobject wallet,
    360     jboolean jlast_used_choice_is_autofill,
    361     jstring jlast_used_account_name,
    362     jstring jlast_used_billing,
    363     jstring jlast_used_shipping,
    364     jstring jlast_used_card) {
    365   const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet);
    366   const std::string google_transaction_id =
    367       AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
    368 
    369   const string16 last_used_account_name =
    370       base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
    371   const std::string last_used_billing =
    372       base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
    373   const std::string last_used_shipping =
    374       base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
    375   const std::string last_used_card =
    376       base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
    377 
    378   scoped_ptr<wallet::FullWallet> full_wallet =
    379       AutofillDialogResult::ConvertFromJava(env, wallet);
    380   FillOutputForSection(
    381       SECTION_EMAIL, form_structure_, full_wallet.get(), email);
    382   FillOutputForSection(
    383       SECTION_CC_BILLING, form_structure_, full_wallet.get(), email);
    384   FillOutputForSection(
    385       SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
    386 
    387   {
    388     DictionaryPrefUpdate updater(profile_->GetPrefs(),
    389                                  ::prefs::kAutofillDialogDefaults);
    390     base::DictionaryValue* defaults = updater.Get();
    391     if (defaults) {
    392       const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
    393       defaults->SetString(kLastUsedAccountName, last_used_account_name);
    394       defaults->SetBoolean(kLastUsedChoiceIsAutofill,
    395                            last_used_choice_is_autofill);
    396       if (!last_used_billing.empty())
    397         defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
    398       if (!last_used_shipping.empty())
    399         defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
    400       if (!last_used_card.empty())
    401         defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
    402     } else {
    403       LOG(ERROR) << "Failed to save AutofillDialog preferences";
    404     }
    405   }
    406 
    407   if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT) {
    408     autocheckout_started_timestamp_ = base::Time::Now();
    409     SetAutocheckoutState(AUTOCHECKOUT_IN_PROGRESS);
    410   }
    411 
    412   LogOnFinishSubmitMetrics();
    413 
    414   // Callback should be called as late as possible.
    415   callback_.Run(&form_structure_, google_transaction_id);
    416 
    417   // This might delete us.
    418   if (GetDialogType() == DIALOG_TYPE_REQUEST_AUTOCOMPLETE)
    419     Hide();
    420 }
    421 
    422 AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
    423     content::WebContents* contents,
    424     const FormData& form_structure,
    425     const GURL& source_url,
    426     const DialogType dialog_type,
    427     const base::Callback<void(const FormStructure*,
    428                               const std::string&)>& callback)
    429     : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
    430       contents_(contents),
    431       initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
    432       dialog_type_(dialog_type),
    433       form_structure_(form_structure, std::string()),
    434       invoked_from_same_origin_(true),
    435       source_url_(source_url),
    436       callback_(callback),
    437       cares_about_shipping_(true),
    438       weak_ptr_factory_(this),
    439       autocheckout_state_(AUTOCHECKOUT_NOT_STARTED),
    440       was_ui_latency_logged_(false) {
    441   DCHECK(!callback_.is_null());
    442 }
    443 
    444 bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const {
    445   DCHECK_GT(form_structure_.field_count(), 0U);
    446 
    447   for (size_t i = 0; i < form_structure_.field_count(); ++i) {
    448     AutofillType type = form_structure_.field(i)->Type();
    449     if (common::IsCreditCardType(type.GetStorableType()))
    450       return true;
    451   }
    452 
    453   return false;
    454 }
    455 
    456 bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const {
    457   return source_url_.SchemeIs(chrome::kHttpsScheme);
    458 }
    459 
    460 void AutofillDialogControllerAndroid::SetAutocheckoutState(
    461     AutocheckoutState autocheckout_state) {
    462   if (autocheckout_state_ == autocheckout_state)
    463     return;
    464 
    465   autocheckout_state_ = autocheckout_state;
    466 }
    467 
    468 void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
    469   GetMetricLogger().LogDialogUiDuration(
    470       base::Time::Now() - dialog_shown_timestamp_,
    471       GetDialogType(),
    472       AutofillMetrics::DIALOG_ACCEPTED);
    473 
    474   GetMetricLogger().LogDialogUiEvent(
    475       GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED);
    476 }
    477 
    478 void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
    479   GetMetricLogger().LogDialogUiDuration(
    480       base::Time::Now() - dialog_shown_timestamp_,
    481       GetDialogType(),
    482       AutofillMetrics::DIALOG_CANCELED);
    483 
    484   GetMetricLogger().LogDialogUiEvent(
    485       GetDialogType(), AutofillMetrics::DIALOG_UI_CANCELED);
    486 }
    487 
    488 }  // namespace autofill
    489