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