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