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