autofill_dialog_controller_android.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/common/pref_names.h" 24#include "chrome/common/url_constants.h" 25#include "components/autofill/content/browser/wallet/full_wallet.h" 26#include "components/autofill/core/browser/autofill_metrics.h" 27#include "components/autofill/core/browser/autofill_profile.h" 28#include "components/autofill/core/browser/autofill_type.h" 29#include "components/autofill/core/browser/credit_card.h" 30#include "components/autofill/core/browser/personal_data_manager.h" 31#include "components/autofill/core/common/form_data.h" 32#include "components/pref_registry/pref_registry_syncable.h" 33#include "content/public/browser/navigation_controller.h" 34#include "content/public/browser/navigation_details.h" 35#include "content/public/browser/navigation_entry.h" 36#include "content/public/browser/web_contents.h" 37#include "jni/AutofillDialogControllerAndroid_jni.h" 38#include "ui/base/android/window_android.h" 39#include "ui/base/models/combobox_model.h" 40#include "ui/base/models/menu_model.h" 41#include "ui/gfx/android/java_bitmap.h" 42#include "ui/gfx/rect.h" 43#include "url/gurl.h" 44 45namespace autofill { 46 47namespace { 48 49using wallet::FullWallet; 50 51// Keys in kAutofillDialogDefaults pref dictionary (do not change these values). 52const char kLastUsedAccountName[] = "last_used_account_name"; 53const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill"; 54const char kLastUsedBillingAddressGuid[] = "last_used_billing"; 55const char kLastUsedShippingAddressGuid[] = "last_used_shipping"; 56const char kLastUsedCreditCardGuid[] = "last_used_card"; 57 58// Constructs |inputs| for the SECTION_CC_BILLING section. 59void BuildCcBillingInputs(DetailInputs* inputs) { 60 const DetailInput kCcBillingInputs[] = { 61 { DetailInput::LONG, NAME_BILLING_FULL }, 62 { DetailInput::LONG, ADDRESS_BILLING_STREET_ADDRESS }, 63 { DetailInput::LONG, ADDRESS_BILLING_CITY }, 64 { DetailInput::LONG, ADDRESS_BILLING_DEPENDENT_LOCALITY }, 65 { DetailInput::LONG, ADDRESS_BILLING_STATE }, 66 { DetailInput::LONG, ADDRESS_BILLING_ZIP }, 67 { DetailInput::LONG, ADDRESS_BILLING_SORTING_CODE }, 68 { DetailInput::LONG, ADDRESS_BILLING_COUNTRY }, 69 { DetailInput::LONG, PHONE_BILLING_WHOLE_NUMBER }, 70 { DetailInput::LONG, CREDIT_CARD_NUMBER }, 71 { DetailInput::LONG, CREDIT_CARD_EXP_MONTH }, 72 { DetailInput::LONG, CREDIT_CARD_EXP_4_DIGIT_YEAR }, 73 { DetailInput::LONG, CREDIT_CARD_VERIFICATION_CODE }, 74 }; 75 common::BuildInputs(kCcBillingInputs, arraysize(kCcBillingInputs), inputs); 76} 77 78// Constructs |inputs| for the SECTION_SHIPPING section. 79void BuildShippingInputs(DetailInputs* inputs) { 80 const DetailInput kShippingInputs[] = { 81 { DetailInput::LONG, NAME_FULL }, 82 { DetailInput::LONG, ADDRESS_HOME_STREET_ADDRESS }, 83 { DetailInput::LONG, ADDRESS_HOME_CITY }, 84 { DetailInput::LONG, ADDRESS_HOME_DEPENDENT_LOCALITY }, 85 { DetailInput::LONG, ADDRESS_HOME_STATE }, 86 { DetailInput::LONG, ADDRESS_HOME_ZIP }, 87 { DetailInput::LONG, ADDRESS_HOME_SORTING_CODE }, 88 { DetailInput::LONG, ADDRESS_HOME_COUNTRY }, 89 { DetailInput::LONG, PHONE_HOME_WHOLE_NUMBER }, 90 }; 91 common::BuildInputs(kShippingInputs, arraysize(kShippingInputs), inputs); 92} 93 94base::string16 NullGetInfo(const AutofillType& type) { 95 return base::string16(); 96} 97 98void FillOutputForSectionWithComparator( 99 DialogSection section, 100 const DetailInputs& inputs, 101 const FormStructure::InputFieldComparator& compare, 102 FormStructure& form_structure, 103 FullWallet* full_wallet, 104 const base::string16& email_address) { 105 if ((section == SECTION_CC_BILLING && !full_wallet->billing_address()) || 106 (section == SECTION_SHIPPING && !full_wallet->shipping_address())) { 107 return; 108 } 109 110 base::Callback<base::string16(const AutofillType&)> get_info = 111 base::Bind(&FullWallet::GetInfo, 112 base::Unretained(full_wallet), 113 g_browser_process->GetApplicationLocale()); 114 115 std::vector<ServerFieldType> types = common::TypesFromInputs(inputs); 116 form_structure.FillFields( 117 types, 118 compare, 119 get_info, 120 section == SECTION_CC_BILLING 121 ? full_wallet->billing_address()->language_code() 122 : full_wallet->shipping_address()->language_code(), 123 g_browser_process->GetApplicationLocale()); 124} 125 126void FillOutputForSection( 127 DialogSection section, 128 FormStructure& form_structure, 129 FullWallet* full_wallet, 130 const base::string16& email_address) { 131 DCHECK(section == SECTION_CC_BILLING || section == SECTION_SHIPPING); 132 DetailInputs inputs; 133 if (section == SECTION_CC_BILLING) 134 BuildCcBillingInputs(&inputs); 135 else 136 BuildShippingInputs(&inputs); 137 138 FillOutputForSectionWithComparator( 139 section, inputs, 140 base::Bind(common::ServerTypeMatchesField, section), 141 form_structure, full_wallet, email_address); 142 143 if (section == SECTION_CC_BILLING) { 144 // Email is hidden while using Wallet, special case it. 145 for (size_t i = 0; i < form_structure.field_count(); ++i) { 146 AutofillField* field = form_structure.field(i); 147 if (field->Type().GetStorableType() == EMAIL_ADDRESS) 148 field->value = email_address; 149 } 150 } 151} 152 153// Returns true if |input_type| in |section| is needed for |form_structure|. 154bool IsSectionInputUsedInFormStructure(DialogSection section, 155 ServerFieldType input_type, 156 const FormStructure& form_structure) { 157 for (size_t i = 0; i < form_structure.field_count(); ++i) { 158 const AutofillField* field = form_structure.field(i); 159 if (field && common::ServerTypeMatchesField(section, input_type, *field)) 160 return true; 161 } 162 return false; 163} 164 165// Returns true if one of |inputs| in |section| is needed for |form_structure|. 166bool IsSectionInputsUsedInFormStructure(DialogSection section, 167 const ServerFieldType* input_types, 168 const size_t input_types_size, 169 const FormStructure& form_structure) { 170 for (size_t i = 0; i < input_types_size; ++i) { 171 if (IsSectionInputUsedInFormStructure( 172 section, input_types[i], form_structure)) { 173 return true; 174 } 175 } 176 return false; 177} 178 179} // namespace 180 181 182// static 183base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create( 184 content::WebContents* contents, 185 const FormData& form_structure, 186 const GURL& source_url, 187 const AutofillClient::ResultCallback& callback) { 188 // AutofillDialogControllerAndroid owns itself. 189 AutofillDialogControllerAndroid* autofill_dialog_controller = 190 new AutofillDialogControllerAndroid(contents, 191 form_structure, 192 source_url, 193 callback); 194 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr(); 195} 196 197#if defined(ENABLE_AUTOFILL_DIALOG) 198// static 199base::WeakPtr<AutofillDialogController> 200AutofillDialogController::Create( 201 content::WebContents* contents, 202 const FormData& form_structure, 203 const GURL& source_url, 204 const AutofillClient::ResultCallback& callback) { 205 return AutofillDialogControllerAndroid::Create(contents, 206 form_structure, 207 source_url, 208 callback); 209} 210 211// static 212void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {} 213 214// static 215void AutofillDialogController::RegisterProfilePrefs( 216 user_prefs::PrefRegistrySyncable* registry) { 217 registry->RegisterDictionaryPref( 218 ::prefs::kAutofillDialogDefaults, 219 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 220} 221#endif // defined(ENABLE_AUTOFILL_DIALOG) 222 223AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() { 224 if (java_object_.is_null()) 225 return; 226 227 JNIEnv* env = base::android::AttachCurrentThread(); 228 Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj()); 229} 230 231void AutofillDialogControllerAndroid::Show() { 232 JNIEnv* env = base::android::AttachCurrentThread(); 233 dialog_shown_timestamp_ = base::Time::Now(); 234 235 // The Autofill dialog is shown in response to a message from the renderer and 236 // as such, it can only be made in the context of the current document. A call 237 // to GetActiveEntry would return a pending entry, if there was one, which 238 // would be a security bug. Therefore, we use the last committed URL for the 239 // access checks. 240 const GURL& current_url = contents_->GetLastCommittedURL(); 241 invoked_from_same_origin_ = 242 current_url.GetOrigin() == source_url_.GetOrigin(); 243 244 // Fail if the dialog factory (e.g. SDK) doesn't support cross-origin calls. 245 if (!Java_AutofillDialogControllerAndroid_isDialogAllowed( 246 env, 247 invoked_from_same_origin_)) { 248 callback_.Run( 249 AutofillClient::AutocompleteResultErrorDisabled, 250 base::ASCIIToUTF16("Cross-origin form invocations are not supported."), 251 NULL); 252 delete this; 253 return; 254 } 255 256 // Determine what field types should be included in the dialog. 257 bool has_types = false; 258 bool has_sections = false; 259 form_structure_.ParseFieldTypesFromAutocompleteAttributes( 260 &has_types, &has_sections); 261 262 // Fail if the author didn't specify autocomplete types, or 263 // if the dialog shouldn't be shown in a given circumstances. 264 if (!has_types) { 265 callback_.Run( 266 AutofillClient::AutocompleteResultErrorDisabled, 267 base::ASCIIToUTF16("Form is missing autocomplete attributes."), 268 NULL); 269 delete this; 270 return; 271 } 272 273 // Fail if the author didn't ask for at least some kind of credit card 274 // information. 275 bool has_credit_card_field = false; 276 for (size_t i = 0; i < form_structure_.field_count(); ++i) { 277 AutofillType type = form_structure_.field(i)->Type(); 278 if (type.html_type() != HTML_TYPE_UNKNOWN && type.group() == CREDIT_CARD) { 279 has_credit_card_field = true; 280 break; 281 } 282 } 283 284 if (!has_credit_card_field) { 285 callback_.Run( 286 AutofillClient::AutocompleteResultErrorDisabled, 287 base::ASCIIToUTF16("Form is not a payment form (must contain " 288 "some autocomplete=\"cc-*\" fields). "), 289 NULL); 290 delete this; 291 return; 292 } 293 294 // Log any relevant UI metrics and security exceptions. 295 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN); 296 297 GetMetricLogger().LogDialogSecurityMetric( 298 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN); 299 300 if (!invoked_from_same_origin_) { 301 GetMetricLogger().LogDialogSecurityMetric( 302 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME); 303 } 304 305 const ServerFieldType full_billing_is_necessary_if[] = { 306 ADDRESS_BILLING_LINE1, 307 ADDRESS_BILLING_LINE2, 308 ADDRESS_BILLING_APT_NUM, 309 ADDRESS_BILLING_CITY, 310 ADDRESS_BILLING_STATE, 311 // ADDRESS_BILLING_ZIP, // Postal code alone is a short form. 312 ADDRESS_BILLING_COUNTRY, 313 ADDRESS_BILLING_STREET_ADDRESS, 314 ADDRESS_BILLING_DEPENDENT_LOCALITY, 315 ADDRESS_BILLING_SORTING_CODE, 316 PHONE_BILLING_WHOLE_NUMBER 317 }; 318 const ServerFieldType billing_phone_number_is_necessary_if[] = { 319 PHONE_BILLING_WHOLE_NUMBER 320 }; 321 const ServerFieldType shipping_phone_number_is_necessary_if[] = { 322 PHONE_HOME_WHOLE_NUMBER 323 }; 324 const bool request_full_billing_address = 325 IsSectionInputsUsedInFormStructure( 326 SECTION_BILLING, 327 full_billing_is_necessary_if, 328 arraysize(full_billing_is_necessary_if), 329 form_structure_); 330 const bool request_phone_numbers = 331 IsSectionInputsUsedInFormStructure( 332 SECTION_BILLING, 333 billing_phone_number_is_necessary_if, 334 arraysize(billing_phone_number_is_necessary_if), 335 form_structure_) || 336 IsSectionInputsUsedInFormStructure( 337 SECTION_SHIPPING, 338 shipping_phone_number_is_necessary_if, 339 arraysize(shipping_phone_number_is_necessary_if), 340 form_structure_); 341 342 bool request_shipping_address = false; 343 { 344 DetailInputs inputs; 345 BuildShippingInputs(&inputs); 346 request_shipping_address = form_structure_.FillFields( 347 common::TypesFromInputs(inputs), 348 base::Bind(common::ServerTypeMatchesField, SECTION_SHIPPING), 349 base::Bind(NullGetInfo), 350 std::string(), 351 g_browser_process->GetApplicationLocale()); 352 } 353 354 bool last_used_choice_is_autofill = false; 355 base::string16 last_used_account_name; 356 std::string last_used_billing; 357 std::string last_used_shipping; 358 std::string last_used_credit_card; 359 { 360 const base::DictionaryValue* defaults = 361 profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults); 362 if (defaults) { 363 defaults->GetString(kLastUsedAccountName, &last_used_account_name); 364 defaults->GetBoolean(kLastUsedChoiceIsAutofill, 365 &last_used_choice_is_autofill); 366 defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing); 367 defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping); 368 defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card); 369 } else { 370 DLOG(ERROR) << "Failed to read AutofillDialog preferences"; 371 } 372 } 373 374 const bool incognito_mode = profile_->IsOffTheRecord(); 375 if (incognito_mode) 376 last_used_choice_is_autofill = true; 377 378 ScopedJavaLocalRef<jstring> jlast_used_account_name = 379 base::android::ConvertUTF16ToJavaString( 380 env, last_used_account_name); 381 ScopedJavaLocalRef<jstring> jlast_used_billing = 382 base::android::ConvertUTF8ToJavaString( 383 env, last_used_billing); 384 ScopedJavaLocalRef<jstring> jlast_used_shipping = 385 base::android::ConvertUTF8ToJavaString( 386 env, last_used_shipping); 387 ScopedJavaLocalRef<jstring> jlast_used_card = 388 base::android::ConvertUTF8ToJavaString( 389 env, last_used_credit_card); 390 ScopedJavaLocalRef<jstring> jmerchant_domain = 391 base::android::ConvertUTF8ToJavaString( 392 env, source_url_.GetOrigin().spec()); 393 const std::set<base::string16> available_shipping_countries = 394 form_structure_.PossibleValues(ADDRESS_HOME_COUNTRY); 395 ScopedJavaLocalRef<jobjectArray> jshipping_countries = 396 base::android::ToJavaArrayOfStrings( 397 env, 398 std::vector<base::string16>(available_shipping_countries.begin(), 399 available_shipping_countries.end())); 400 const std::set<base::string16> available_credit_card_types = 401 form_structure_.PossibleValues(CREDIT_CARD_TYPE); 402 ScopedJavaLocalRef<jobjectArray> jcredit_card_types = 403 base::android::ToJavaArrayOfStrings( 404 env, 405 std::vector<base::string16>(available_credit_card_types.begin(), 406 available_credit_card_types.end())); 407 408 java_object_.Reset(Java_AutofillDialogControllerAndroid_create( 409 env, 410 reinterpret_cast<intptr_t>(this), 411 WindowAndroidHelper::FromWebContents(contents_)-> 412 GetWindowAndroid()->GetJavaObject().obj(), 413 request_full_billing_address, request_shipping_address, 414 request_phone_numbers, incognito_mode, 415 last_used_choice_is_autofill, jlast_used_account_name.obj(), 416 jlast_used_billing.obj(), jlast_used_shipping.obj(), 417 jlast_used_card.obj(), 418 jmerchant_domain.obj(), 419 jshipping_countries.obj(), 420 jcredit_card_types.obj())); 421} 422 423void AutofillDialogControllerAndroid::Hide() { 424 delete this; 425} 426 427void AutofillDialogControllerAndroid::TabActivated() {} 428 429// static 430bool AutofillDialogControllerAndroid:: 431 RegisterAutofillDialogControllerAndroid(JNIEnv* env) { 432 return RegisterNativesImpl(env); 433} 434 435void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env, 436 jobject obj) { 437 LogOnCancelMetrics(); 438 callback_.Run(AutofillClient::AutocompleteResultErrorCancel, 439 base::string16(), 440 NULL); 441} 442 443void AutofillDialogControllerAndroid::DialogContinue( 444 JNIEnv* env, 445 jobject obj, 446 jobject wallet, 447 jboolean jlast_used_choice_is_autofill, 448 jstring jlast_used_account_name, 449 jstring jlast_used_billing, 450 jstring jlast_used_shipping, 451 jstring jlast_used_card) { 452 const base::string16 email = 453 AutofillDialogResult::GetWalletEmail(env, wallet); 454 const std::string google_transaction_id = 455 AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet); 456 457 const base::string16 last_used_account_name = 458 base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name); 459 const std::string last_used_billing = 460 base::android::ConvertJavaStringToUTF8(env, jlast_used_billing); 461 const std::string last_used_shipping = 462 base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping); 463 const std::string last_used_card = 464 base::android::ConvertJavaStringToUTF8(env, jlast_used_card); 465 466 scoped_ptr<FullWallet> full_wallet = 467 AutofillDialogResult::ConvertFromJava(env, wallet); 468 FillOutputForSection( 469 SECTION_CC_BILLING, form_structure_, full_wallet.get(), email); 470 FillOutputForSection( 471 SECTION_SHIPPING, form_structure_, full_wallet.get(), email); 472 473 { 474 DictionaryPrefUpdate updater(profile_->GetPrefs(), 475 ::prefs::kAutofillDialogDefaults); 476 base::DictionaryValue* defaults = updater.Get(); 477 if (defaults) { 478 const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill; 479 defaults->SetString(kLastUsedAccountName, last_used_account_name); 480 defaults->SetBoolean(kLastUsedChoiceIsAutofill, 481 last_used_choice_is_autofill); 482 if (!last_used_billing.empty()) 483 defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing); 484 if (!last_used_shipping.empty()) 485 defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping); 486 if (!last_used_card.empty()) 487 defaults->SetString(kLastUsedCreditCardGuid, last_used_card); 488 } else { 489 DLOG(ERROR) << "Failed to save AutofillDialog preferences"; 490 } 491 } 492 493 LogOnFinishSubmitMetrics(); 494 495 // Callback should be called as late as possible. 496 callback_.Run(AutofillClient::AutocompleteResultSuccess, 497 base::string16(), 498 &form_structure_); 499 500 // This might delete us. 501 Hide(); 502} 503 504AutofillDialogControllerAndroid::AutofillDialogControllerAndroid( 505 content::WebContents* contents, 506 const FormData& form_structure, 507 const GURL& source_url, 508 const AutofillClient::ResultCallback& callback) 509 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), 510 contents_(contents), 511 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN), 512 form_structure_(form_structure), 513 invoked_from_same_origin_(true), 514 source_url_(source_url), 515 callback_(callback), 516 cares_about_shipping_(true), 517 weak_ptr_factory_(this), 518 was_ui_latency_logged_(false) { 519 DCHECK(!callback_.is_null()); 520} 521 522void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() { 523 GetMetricLogger().LogDialogUiDuration( 524 base::Time::Now() - dialog_shown_timestamp_, 525 AutofillMetrics::DIALOG_ACCEPTED); 526 527 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED); 528} 529 530void AutofillDialogControllerAndroid::LogOnCancelMetrics() { 531 GetMetricLogger().LogDialogUiDuration( 532 base::Time::Now() - dialog_shown_timestamp_, 533 AutofillMetrics::DIALOG_CANCELED); 534 535 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED); 536} 537 538} // namespace autofill 539