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