autofill_dialog_controller_android.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/base/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#if defined(ENABLE_AUTOFILL_DIALOG) 158// static 159base::WeakPtr<AutofillDialogController> 160AutofillDialogController::Create( 161 content::WebContents* contents, 162 const FormData& form_structure, 163 const GURL& source_url, 164 const base::Callback<void(const FormStructure*)>& callback) { 165 return AutofillDialogControllerAndroid::Create(contents, 166 form_structure, 167 source_url, 168 callback); 169} 170 171// static 172void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {} 173 174// static 175void AutofillDialogController::RegisterProfilePrefs( 176 user_prefs::PrefRegistrySyncable* registry) { 177 registry->RegisterDictionaryPref( 178 ::prefs::kAutofillDialogDefaults, 179 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 180} 181#endif // defined(ENABLE_AUTOFILL_DIALOG) 182 183AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() { 184 if (java_object_.is_null()) 185 return; 186 187 JNIEnv* env = base::android::AttachCurrentThread(); 188 Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj()); 189} 190 191void AutofillDialogControllerAndroid::Show() { 192 JNIEnv* env = base::android::AttachCurrentThread(); 193 dialog_shown_timestamp_ = base::Time::Now(); 194 195 const GURL& current_url = contents_->GetLastCommittedURL(); 196 invoked_from_same_origin_ = 197 current_url.GetOrigin() == source_url_.GetOrigin(); 198 199 // Determine what field types should be included in the dialog. 200 bool has_types = false; 201 bool has_sections = false; 202 form_structure_.ParseFieldTypesFromAutocompleteAttributes( 203 &has_types, &has_sections); 204 205 // Fail if the author didn't specify autocomplete types, or 206 // if the dialog shouldn't be shown in a given circumstances. 207 if (!has_types || 208 !Java_AutofillDialogControllerAndroid_isDialogAllowed( 209 env, 210 RequestingCreditCardInfo(), 211 TransmissionWillBeSecure(), 212 invoked_from_same_origin_)) { 213 callback_.Run(NULL); 214 delete this; 215 return; 216 } 217 218 // Log any relevant UI metrics and security exceptions. 219 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN); 220 221 GetMetricLogger().LogDialogSecurityMetric( 222 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN); 223 224 if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) { 225 GetMetricLogger().LogDialogSecurityMetric( 226 AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP); 227 } 228 229 if (!invoked_from_same_origin_) { 230 GetMetricLogger().LogDialogSecurityMetric( 231 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME); 232 } 233 234 const ServerFieldType full_billing_is_necessary_if[] = { 235 ADDRESS_BILLING_LINE1, 236 ADDRESS_BILLING_LINE2, 237 ADDRESS_BILLING_CITY, 238 ADDRESS_BILLING_STATE, 239 PHONE_BILLING_WHOLE_NUMBER 240 }; 241 const bool request_full_billing_address = 242 IsSectionInputsUsedInFormStructure( 243 SECTION_BILLING, 244 full_billing_is_necessary_if, 245 arraysize(full_billing_is_necessary_if), 246 form_structure_); 247 const bool request_phone_numbers = 248 IsSectionInputUsedInFormStructure( 249 SECTION_BILLING, 250 PHONE_BILLING_WHOLE_NUMBER, 251 form_structure_) || 252 IsSectionInputUsedInFormStructure( 253 SECTION_SHIPPING, 254 PHONE_HOME_WHOLE_NUMBER, 255 form_structure_); 256 257 bool request_shipping_address = false; 258 { 259 DetailInputs inputs; 260 common::BuildInputsForSection(SECTION_SHIPPING, &inputs); 261 EmptyDataModelWrapper empty_wrapper; 262 request_shipping_address = empty_wrapper.FillFormStructure( 263 inputs, 264 base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING), 265 &form_structure_); 266 } 267 268 const bool incognito_mode = profile_->IsOffTheRecord(); 269 270 bool last_used_choice_is_autofill = false; 271 base::string16 last_used_account_name; 272 std::string last_used_billing; 273 std::string last_used_shipping; 274 std::string last_used_credit_card; 275 { 276 const base::DictionaryValue* defaults = 277 profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults); 278 if (defaults) { 279 defaults->GetString(kLastUsedAccountName, &last_used_account_name); 280 defaults->GetBoolean(kLastUsedChoiceIsAutofill, 281 &last_used_choice_is_autofill); 282 defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing); 283 defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping); 284 defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card); 285 } else { 286 DLOG(ERROR) << "Failed to read AutofillDialog preferences"; 287 } 288 } 289 290 if (contents_->GetBrowserContext()->IsOffTheRecord()) 291 last_used_choice_is_autofill = true; 292 293 ScopedJavaLocalRef<jstring> jlast_used_account_name = 294 base::android::ConvertUTF16ToJavaString( 295 env, last_used_account_name); 296 ScopedJavaLocalRef<jstring> jlast_used_billing = 297 base::android::ConvertUTF8ToJavaString( 298 env, last_used_billing); 299 ScopedJavaLocalRef<jstring> jlast_used_shipping = 300 base::android::ConvertUTF8ToJavaString( 301 env, last_used_shipping); 302 ScopedJavaLocalRef<jstring> jlast_used_card = 303 base::android::ConvertUTF8ToJavaString( 304 env, last_used_credit_card); 305 ScopedJavaLocalRef<jstring> jmerchant_domain = 306 base::android::ConvertUTF8ToJavaString( 307 env, source_url_.GetOrigin().spec()); 308 java_object_.Reset(Java_AutofillDialogControllerAndroid_create( 309 env, 310 reinterpret_cast<intptr_t>(this), 311 WindowAndroidHelper::FromWebContents(contents_)-> 312 GetWindowAndroid()->GetJavaObject().obj(), 313 request_full_billing_address, request_shipping_address, 314 request_phone_numbers, incognito_mode, 315 last_used_choice_is_autofill, jlast_used_account_name.obj(), 316 jlast_used_billing.obj(), jlast_used_shipping.obj(), 317 jlast_used_card.obj(), 318 jmerchant_domain.obj())); 319} 320 321void AutofillDialogControllerAndroid::Hide() { 322 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 323 NOTIMPLEMENTED(); 324} 325 326void AutofillDialogControllerAndroid::TabActivated() {} 327 328// static 329bool AutofillDialogControllerAndroid:: 330 RegisterAutofillDialogControllerAndroid(JNIEnv* env) { 331 return RegisterNativesImpl(env); 332} 333 334void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env, 335 jobject obj) { 336 LogOnCancelMetrics(); 337 callback_.Run(NULL); 338} 339 340void AutofillDialogControllerAndroid::DialogContinue( 341 JNIEnv* env, 342 jobject obj, 343 jobject wallet, 344 jboolean jlast_used_choice_is_autofill, 345 jstring jlast_used_account_name, 346 jstring jlast_used_billing, 347 jstring jlast_used_shipping, 348 jstring jlast_used_card) { 349 const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet); 350 const std::string google_transaction_id = 351 AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet); 352 353 const string16 last_used_account_name = 354 base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name); 355 const std::string last_used_billing = 356 base::android::ConvertJavaStringToUTF8(env, jlast_used_billing); 357 const std::string last_used_shipping = 358 base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping); 359 const std::string last_used_card = 360 base::android::ConvertJavaStringToUTF8(env, jlast_used_card); 361 362 scoped_ptr<wallet::FullWallet> full_wallet = 363 AutofillDialogResult::ConvertFromJava(env, wallet); 364 FillOutputForSection( 365 SECTION_CC_BILLING, form_structure_, full_wallet.get(), email); 366 FillOutputForSection( 367 SECTION_SHIPPING, form_structure_, full_wallet.get(), email); 368 369 { 370 DictionaryPrefUpdate updater(profile_->GetPrefs(), 371 ::prefs::kAutofillDialogDefaults); 372 base::DictionaryValue* defaults = updater.Get(); 373 if (defaults) { 374 const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill; 375 defaults->SetString(kLastUsedAccountName, last_used_account_name); 376 defaults->SetBoolean(kLastUsedChoiceIsAutofill, 377 last_used_choice_is_autofill); 378 if (!last_used_billing.empty()) 379 defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing); 380 if (!last_used_shipping.empty()) 381 defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping); 382 if (!last_used_card.empty()) 383 defaults->SetString(kLastUsedCreditCardGuid, last_used_card); 384 } else { 385 LOG(ERROR) << "Failed to save AutofillDialog preferences"; 386 } 387 } 388 389 LogOnFinishSubmitMetrics(); 390 391 // Callback should be called as late as possible. 392 callback_.Run(&form_structure_); 393 394 // This might delete us. 395 Hide(); 396} 397 398AutofillDialogControllerAndroid::AutofillDialogControllerAndroid( 399 content::WebContents* contents, 400 const FormData& form_structure, 401 const GURL& source_url, 402 const base::Callback<void(const FormStructure*)>& callback) 403 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), 404 contents_(contents), 405 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN), 406 form_structure_(form_structure), 407 invoked_from_same_origin_(true), 408 source_url_(source_url), 409 callback_(callback), 410 cares_about_shipping_(true), 411 weak_ptr_factory_(this), 412 was_ui_latency_logged_(false) { 413 DCHECK(!callback_.is_null()); 414} 415 416bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const { 417 DCHECK_GT(form_structure_.field_count(), 0U); 418 419 for (size_t i = 0; i < form_structure_.field_count(); ++i) { 420 AutofillType type = form_structure_.field(i)->Type(); 421 if (common::IsCreditCardType(type.GetStorableType())) 422 return true; 423 } 424 425 return false; 426} 427 428bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const { 429 return source_url_.SchemeIs(content::kHttpsScheme); 430} 431 432void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() { 433 GetMetricLogger().LogDialogUiDuration( 434 base::Time::Now() - dialog_shown_timestamp_, 435 AutofillMetrics::DIALOG_ACCEPTED); 436 437 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED); 438} 439 440void AutofillDialogControllerAndroid::LogOnCancelMetrics() { 441 GetMetricLogger().LogDialogUiDuration( 442 base::Time::Now() - dialog_shown_timestamp_, 443 AutofillMetrics::DIALOG_CANCELED); 444 445 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED); 446} 447 448} // namespace autofill 449