autofill_dialog_controller_android.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/prefs/scoped_user_pref_update.h" 15#include "base/strings/utf_string_conversions.h" 16#include "chrome/browser/autofill/personal_data_manager_factory.h" 17#include "chrome/browser/browser_process.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 48namespace autofill { 49 50namespace { 51 52// Keys in kAutofillDialogDefaults pref dictionary (do not change these values). 53const char kLastUsedAccountName[] = "last_used_account_name"; 54const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill"; 55const char kLastUsedBillingAddressGuid[] = "last_used_billing"; 56const char kLastUsedShippingAddressGuid[] = "last_used_shipping"; 57const char kLastUsedCreditCardGuid[] = "last_used_card"; 58 59scoped_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 79void 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 scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section, full_wallet); 85 if (wrapper) 86 wrapper->FillFormStructure(inputs, compare, &form_structure); 87} 88 89void FillOutputForSection( 90 DialogSection section, 91 FormStructure& form_structure, 92 wallet::FullWallet* full_wallet, 93 const base::string16& email_address) { 94 DetailInputs inputs; 95 common::BuildInputsForSection(section, &inputs); 96 97 FillOutputForSectionWithComparator( 98 section, inputs, 99 base::Bind(common::DetailInputMatchesField, section), 100 form_structure, full_wallet, email_address); 101 102 if (section == SECTION_CC_BILLING) { 103 // Email is hidden while using Wallet, special case it. 104 for (size_t i = 0; i < form_structure.field_count(); ++i) { 105 AutofillField* field = form_structure.field(i); 106 if (field->Type().GetStorableType() == EMAIL_ADDRESS) 107 field->value = email_address; 108 } 109 } 110} 111 112// Returns true if |input_type| in |section| is needed for |form_structure|. 113bool IsSectionInputUsedInFormStructure(DialogSection section, 114 ServerFieldType input_type, 115 const FormStructure& form_structure) { 116 const DetailInput input = { 0, input_type }; 117 for (size_t i = 0; i < form_structure.field_count(); ++i) { 118 const AutofillField* field = form_structure.field(i); 119 if (field && common::DetailInputMatchesField(section, input, *field)) 120 return true; 121 } 122 return false; 123} 124 125// Returns true if one of |inputs| in |section| is needed for |form_structure|. 126bool IsSectionInputsUsedInFormStructure(DialogSection section, 127 const ServerFieldType* input_types, 128 const size_t input_types_size, 129 const FormStructure& form_structure) { 130 for (size_t i = 0; i < input_types_size; ++i) { 131 if (IsSectionInputUsedInFormStructure( 132 section, input_types[i], form_structure)) { 133 return true; 134 } 135 } 136 return false; 137} 138 139} // namespace 140 141 142// static 143base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create( 144 content::WebContents* contents, 145 const FormData& form_structure, 146 const GURL& source_url, 147 const base::Callback<void(const FormStructure*)>& callback) { 148 // AutofillDialogControllerAndroid owns itself. 149 AutofillDialogControllerAndroid* autofill_dialog_controller = 150 new AutofillDialogControllerAndroid(contents, 151 form_structure, 152 source_url, 153 callback); 154 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr(); 155} 156 157// static 158void AutofillDialogControllerAndroid::RegisterProfilePrefs( 159 user_prefs::PrefRegistrySyncable* registry) { 160 registry->RegisterDictionaryPref( 161 ::prefs::kAutofillDialogDefaults, 162 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 163} 164 165#if defined(ENABLE_AUTOFILL_DIALOG) 166// static 167base::WeakPtr<AutofillDialogController> 168AutofillDialogController::Create( 169 content::WebContents* contents, 170 const FormData& form_structure, 171 const GURL& source_url, 172 const base::Callback<void(const FormStructure*)>& callback) { 173 return AutofillDialogControllerAndroid::Create(contents, 174 form_structure, 175 source_url, 176 callback); 177} 178 179// static 180void AutofillDialogController::RegisterProfilePrefs( 181 user_prefs::PrefRegistrySyncable* registry) { 182 AutofillDialogControllerAndroid::RegisterProfilePrefs(registry); 183} 184#endif // defined(ENABLE_AUTOFILL_DIALOG) 185 186AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() { 187 if (java_object_.is_null()) 188 return; 189 190 JNIEnv* env = base::android::AttachCurrentThread(); 191 Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj()); 192} 193 194void AutofillDialogControllerAndroid::Show() { 195 JNIEnv* env = base::android::AttachCurrentThread(); 196 dialog_shown_timestamp_ = base::Time::Now(); 197 198 content::NavigationEntry* entry = contents_->GetController().GetActiveEntry(); 199 const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL(); 200 invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin(); 201 202 // Determine what field types should be included in the dialog. 203 bool has_types = false; 204 bool has_sections = false; 205 form_structure_.ParseFieldTypesFromAutocompleteAttributes( 206 &has_types, &has_sections); 207 208 // Fail if the author didn't specify autocomplete types, or 209 // if the dialog shouldn't be shown in a given circumstances. 210 if (!has_types || 211 !Java_AutofillDialogControllerAndroid_isDialogAllowed( 212 env, 213 RequestingCreditCardInfo(), 214 TransmissionWillBeSecure(), 215 invoked_from_same_origin_)) { 216 callback_.Run(NULL); 217 delete this; 218 return; 219 } 220 221 // Log any relevant UI metrics and security exceptions. 222 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN); 223 224 GetMetricLogger().LogDialogSecurityMetric( 225 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN); 226 227 if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) { 228 GetMetricLogger().LogDialogSecurityMetric( 229 AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP); 230 } 231 232 if (!invoked_from_same_origin_) { 233 GetMetricLogger().LogDialogSecurityMetric( 234 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME); 235 } 236 237 const ServerFieldType full_billing_is_necessary_if[] = { 238 ADDRESS_BILLING_LINE1, 239 ADDRESS_BILLING_LINE2, 240 ADDRESS_BILLING_CITY, 241 ADDRESS_BILLING_STATE, 242 PHONE_BILLING_WHOLE_NUMBER 243 }; 244 const bool request_full_billing_address = 245 IsSectionInputsUsedInFormStructure( 246 SECTION_BILLING, 247 full_billing_is_necessary_if, 248 arraysize(full_billing_is_necessary_if), 249 form_structure_); 250 const bool request_phone_numbers = 251 IsSectionInputUsedInFormStructure( 252 SECTION_BILLING, 253 PHONE_BILLING_WHOLE_NUMBER, 254 form_structure_) || 255 IsSectionInputUsedInFormStructure( 256 SECTION_SHIPPING, 257 PHONE_HOME_WHOLE_NUMBER, 258 form_structure_); 259 260 bool request_shipping_address = false; 261 { 262 DetailInputs inputs; 263 common::BuildInputsForSection(SECTION_SHIPPING, &inputs); 264 EmptyDataModelWrapper empty_wrapper; 265 request_shipping_address = empty_wrapper.FillFormStructure( 266 inputs, 267 base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING), 268 &form_structure_); 269 } 270 271 const bool incognito_mode = profile_->IsOffTheRecord(); 272 273 bool last_used_choice_is_autofill = false; 274 base::string16 last_used_account_name; 275 std::string last_used_billing; 276 std::string last_used_shipping; 277 std::string last_used_credit_card; 278 { 279 const base::DictionaryValue* defaults = 280 profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults); 281 if (defaults) { 282 defaults->GetString(kLastUsedAccountName, &last_used_account_name); 283 defaults->GetBoolean(kLastUsedChoiceIsAutofill, 284 &last_used_choice_is_autofill); 285 defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing); 286 defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping); 287 defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card); 288 } else { 289 DLOG(ERROR) << "Failed to read AutofillDialog preferences"; 290 } 291 } 292 293 if (contents_->GetBrowserContext()->IsOffTheRecord()) 294 last_used_choice_is_autofill = true; 295 296 ScopedJavaLocalRef<jstring> jlast_used_account_name = 297 base::android::ConvertUTF16ToJavaString( 298 env, last_used_account_name); 299 ScopedJavaLocalRef<jstring> jlast_used_billing = 300 base::android::ConvertUTF8ToJavaString( 301 env, last_used_billing); 302 ScopedJavaLocalRef<jstring> jlast_used_shipping = 303 base::android::ConvertUTF8ToJavaString( 304 env, last_used_shipping); 305 ScopedJavaLocalRef<jstring> jlast_used_card = 306 base::android::ConvertUTF8ToJavaString( 307 env, last_used_credit_card); 308 ScopedJavaLocalRef<jstring> jmerchant_domain = 309 base::android::ConvertUTF8ToJavaString( 310 env, source_url_.GetOrigin().spec()); 311 java_object_.Reset(Java_AutofillDialogControllerAndroid_create( 312 env, 313 reinterpret_cast<jint>(this), 314 WindowAndroidHelper::FromWebContents(contents_)-> 315 GetWindowAndroid()->GetJavaObject().obj(), 316 request_full_billing_address, request_shipping_address, 317 request_phone_numbers, incognito_mode, 318 last_used_choice_is_autofill, jlast_used_account_name.obj(), 319 jlast_used_billing.obj(), jlast_used_shipping.obj(), 320 jlast_used_card.obj(), 321 jmerchant_domain.obj())); 322} 323 324void AutofillDialogControllerAndroid::Hide() { 325 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 326 NOTIMPLEMENTED(); 327} 328 329void AutofillDialogControllerAndroid::TabActivated() {} 330 331// static 332bool AutofillDialogControllerAndroid:: 333 RegisterAutofillDialogControllerAndroid(JNIEnv* env) { 334 return RegisterNativesImpl(env); 335} 336 337void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env, 338 jobject obj) { 339 LogOnCancelMetrics(); 340 callback_.Run(NULL); 341} 342 343void AutofillDialogControllerAndroid::DialogContinue( 344 JNIEnv* env, 345 jobject obj, 346 jobject wallet, 347 jboolean jlast_used_choice_is_autofill, 348 jstring jlast_used_account_name, 349 jstring jlast_used_billing, 350 jstring jlast_used_shipping, 351 jstring jlast_used_card) { 352 const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet); 353 const std::string google_transaction_id = 354 AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet); 355 356 const string16 last_used_account_name = 357 base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name); 358 const std::string last_used_billing = 359 base::android::ConvertJavaStringToUTF8(env, jlast_used_billing); 360 const std::string last_used_shipping = 361 base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping); 362 const std::string last_used_card = 363 base::android::ConvertJavaStringToUTF8(env, jlast_used_card); 364 365 scoped_ptr<wallet::FullWallet> full_wallet = 366 AutofillDialogResult::ConvertFromJava(env, wallet); 367 FillOutputForSection( 368 SECTION_CC_BILLING, form_structure_, full_wallet.get(), email); 369 FillOutputForSection( 370 SECTION_SHIPPING, form_structure_, full_wallet.get(), email); 371 372 { 373 DictionaryPrefUpdate updater(profile_->GetPrefs(), 374 ::prefs::kAutofillDialogDefaults); 375 base::DictionaryValue* defaults = updater.Get(); 376 if (defaults) { 377 const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill; 378 defaults->SetString(kLastUsedAccountName, last_used_account_name); 379 defaults->SetBoolean(kLastUsedChoiceIsAutofill, 380 last_used_choice_is_autofill); 381 if (!last_used_billing.empty()) 382 defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing); 383 if (!last_used_shipping.empty()) 384 defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping); 385 if (!last_used_card.empty()) 386 defaults->SetString(kLastUsedCreditCardGuid, last_used_card); 387 } else { 388 LOG(ERROR) << "Failed to save AutofillDialog preferences"; 389 } 390 } 391 392 LogOnFinishSubmitMetrics(); 393 394 // Callback should be called as late as possible. 395 callback_.Run(&form_structure_); 396 397 // This might delete us. 398 Hide(); 399} 400 401AutofillDialogControllerAndroid::AutofillDialogControllerAndroid( 402 content::WebContents* contents, 403 const FormData& form_structure, 404 const GURL& source_url, 405 const base::Callback<void(const FormStructure*)>& callback) 406 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), 407 contents_(contents), 408 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN), 409 form_structure_(form_structure), 410 invoked_from_same_origin_(true), 411 source_url_(source_url), 412 callback_(callback), 413 cares_about_shipping_(true), 414 weak_ptr_factory_(this), 415 was_ui_latency_logged_(false) { 416 DCHECK(!callback_.is_null()); 417} 418 419bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const { 420 DCHECK_GT(form_structure_.field_count(), 0U); 421 422 for (size_t i = 0; i < form_structure_.field_count(); ++i) { 423 AutofillType type = form_structure_.field(i)->Type(); 424 if (common::IsCreditCardType(type.GetStorableType())) 425 return true; 426 } 427 428 return false; 429} 430 431bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const { 432 return source_url_.SchemeIs(content::kHttpsScheme); 433} 434 435void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() { 436 GetMetricLogger().LogDialogUiDuration( 437 base::Time::Now() - dialog_shown_timestamp_, 438 AutofillMetrics::DIALOG_ACCEPTED); 439 440 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED); 441} 442 443void AutofillDialogControllerAndroid::LogOnCancelMetrics() { 444 GetMetricLogger().LogDialogUiDuration( 445 base::Time::Now() - dialog_shown_timestamp_, 446 AutofillMetrics::DIALOG_CANCELED); 447 448 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED); 449} 450 451} // namespace autofill 452