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 48 namespace autofill { 49 50 namespace { 51 52 // Keys in kAutofillDialogDefaults pref dictionary (do not change these values). 53 const char kLastUsedAccountName[] = "last_used_account_name"; 54 const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill"; 55 const char kLastUsedBillingAddressGuid[] = "last_used_billing"; 56 const char kLastUsedShippingAddressGuid[] = "last_used_shipping"; 57 const char kLastUsedCreditCardGuid[] = "last_used_card"; 58 59 scoped_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 79 void 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 85 // Email is hidden while using Wallet, special case it. 86 if (section == SECTION_EMAIL) { 87 AutofillProfile profile; 88 profile.SetRawInfo(EMAIL_ADDRESS, email_address); 89 AutofillProfileWrapper profile_wrapper(&profile, 0); 90 profile_wrapper.FillFormStructure(inputs, compare, &form_structure); 91 return; 92 } 93 94 scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section, full_wallet); 95 if (wrapper) 96 wrapper->FillFormStructure(inputs, compare, &form_structure); 97 } 98 99 void FillOutputForSection( 100 DialogSection section, 101 FormStructure& form_structure, 102 wallet::FullWallet* full_wallet, 103 const base::string16& email_address) { 104 DetailInputs inputs; 105 common::BuildInputsForSection(section, &inputs); 106 107 FillOutputForSectionWithComparator( 108 section, inputs, 109 base::Bind(common::DetailInputMatchesField, section), 110 form_structure, full_wallet, email_address); 111 } 112 113 } // namespace 114 115 116 // static 117 base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create( 118 content::WebContents* contents, 119 const FormData& form_structure, 120 const GURL& source_url, 121 const DialogType dialog_type, 122 const base::Callback<void(const FormStructure*, 123 const std::string&)>& callback) { 124 // AutofillDialogControllerAndroid owns itself. 125 AutofillDialogControllerAndroid* autofill_dialog_controller = 126 new AutofillDialogControllerAndroid(contents, 127 form_structure, 128 source_url, 129 dialog_type, 130 callback); 131 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr(); 132 } 133 134 // static 135 void AutofillDialogControllerAndroid::RegisterProfilePrefs( 136 user_prefs::PrefRegistrySyncable* registry) { 137 registry->RegisterDictionaryPref( 138 ::prefs::kAutofillDialogDefaults, 139 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 140 } 141 142 // static 143 base::WeakPtr<AutofillDialogController> 144 AutofillDialogController::Create( 145 content::WebContents* contents, 146 const FormData& form_structure, 147 const GURL& source_url, 148 const DialogType dialog_type, 149 const base::Callback<void(const FormStructure*, 150 const std::string&)>& callback) { 151 return AutofillDialogControllerAndroid::Create(contents, 152 form_structure, 153 source_url, 154 dialog_type, 155 callback); 156 } 157 158 // static 159 void AutofillDialogController::RegisterProfilePrefs( 160 user_prefs::PrefRegistrySyncable* registry) { 161 AutofillDialogControllerAndroid::RegisterProfilePrefs(registry); 162 } 163 164 AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() { 165 JNIEnv* env = base::android::AttachCurrentThread(); 166 Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj()); 167 } 168 169 void AutofillDialogControllerAndroid::Show() { 170 dialog_shown_timestamp_ = base::Time::Now(); 171 172 content::NavigationEntry* entry = contents_->GetController().GetActiveEntry(); 173 const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL(); 174 invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin(); 175 176 // Log any relevant UI metrics and security exceptions. 177 GetMetricLogger().LogDialogUiEvent( 178 GetDialogType(), AutofillMetrics::DIALOG_UI_SHOWN); 179 180 GetMetricLogger().LogDialogSecurityMetric( 181 GetDialogType(), AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN); 182 183 if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) { 184 GetMetricLogger().LogDialogSecurityMetric( 185 GetDialogType(), 186 AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP); 187 } 188 189 if (!invoked_from_same_origin_) { 190 GetMetricLogger().LogDialogSecurityMetric( 191 GetDialogType(), 192 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME); 193 } 194 195 // Determine what field types should be included in the dialog. 196 bool has_types = false; 197 bool has_sections = false; 198 form_structure_.ParseFieldTypesFromAutocompleteAttributes( 199 &has_types, &has_sections); 200 201 // Fail if the author didn't specify autocomplete types. 202 if (!has_types) { 203 callback_.Run(NULL, std::string()); 204 delete this; 205 return; 206 } 207 208 bool request_full_billing_address = true; 209 bool request_shipping_address = false; 210 bool request_phone_numbers = false; 211 212 for (size_t i = 0; i < form_structure_.field_count(); ++i) { 213 const ServerFieldType type = 214 form_structure_.field(i)->Type().GetStorableType(); 215 if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER) { 216 request_phone_numbers = true; 217 } 218 if (type == NAME_FULL || 219 type == ADDRESS_HOME_LINE1 || type == ADDRESS_HOME_LINE2 || 220 type == ADDRESS_HOME_CITY || type == ADDRESS_HOME_STATE || 221 type == ADDRESS_HOME_ZIP || type == ADDRESS_HOME_COUNTRY || 222 type == PHONE_HOME_WHOLE_NUMBER) { 223 request_shipping_address = true; 224 } 225 if (type == ADDRESS_BILLING_LINE1 || type == ADDRESS_BILLING_LINE2 || 226 type == ADDRESS_BILLING_CITY || type == ADDRESS_BILLING_STATE || 227 type == PHONE_BILLING_WHOLE_NUMBER) { 228 request_full_billing_address = true; 229 } 230 } 231 232 if (request_shipping_address) 233 request_full_billing_address = true; 234 235 const bool incognito_mode = profile_->IsOffTheRecord(); 236 237 bool last_used_choice_is_autofill = false; 238 base::string16 last_used_account_name; 239 std::string last_used_billing; 240 std::string last_used_shipping; 241 std::string last_used_credit_card; 242 { 243 const base::DictionaryValue* defaults = 244 profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults); 245 if (defaults) { 246 defaults->GetString(kLastUsedAccountName, &last_used_account_name); 247 defaults->GetBoolean(kLastUsedChoiceIsAutofill, 248 &last_used_choice_is_autofill); 249 defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing); 250 defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping); 251 defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card); 252 } else { 253 DLOG(ERROR) << "Failed to read AutofillDialog preferences"; 254 } 255 } 256 257 if (contents_->GetBrowserContext()->IsOffTheRecord()) 258 last_used_choice_is_autofill = true; 259 260 JNIEnv* env = base::android::AttachCurrentThread(); 261 ScopedJavaLocalRef<jstring> jlast_used_account_name = 262 base::android::ConvertUTF16ToJavaString( 263 env, last_used_account_name); 264 ScopedJavaLocalRef<jstring> jlast_used_billing = 265 base::android::ConvertUTF8ToJavaString( 266 env, last_used_billing); 267 ScopedJavaLocalRef<jstring> jlast_used_shipping = 268 base::android::ConvertUTF8ToJavaString( 269 env, last_used_shipping); 270 ScopedJavaLocalRef<jstring> jlast_used_card = 271 base::android::ConvertUTF8ToJavaString( 272 env, last_used_credit_card); 273 ScopedJavaLocalRef<jstring> jmerchant_domain = 274 base::android::ConvertUTF8ToJavaString( 275 env, source_url_.GetOrigin().spec()); 276 java_object_.Reset(Java_AutofillDialogControllerAndroid_create( 277 env, 278 reinterpret_cast<jint>(this), 279 WindowAndroidHelper::FromWebContents(contents_)-> 280 GetWindowAndroid()->GetJavaObject().obj(), 281 request_full_billing_address, request_shipping_address, 282 request_phone_numbers, incognito_mode, 283 last_used_choice_is_autofill, jlast_used_account_name.obj(), 284 jlast_used_billing.obj(), jlast_used_shipping.obj(), 285 jlast_used_card.obj(), 286 jmerchant_domain.obj())); 287 } 288 289 void AutofillDialogControllerAndroid::Hide() { 290 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 291 NOTIMPLEMENTED(); 292 } 293 294 void AutofillDialogControllerAndroid::TabActivated() {} 295 296 void AutofillDialogControllerAndroid::AddAutocheckoutStep( 297 AutocheckoutStepType step_type) { 298 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 299 NOTIMPLEMENTED() << " step_type = " << step_type; 300 } 301 302 void AutofillDialogControllerAndroid::UpdateAutocheckoutStep( 303 AutocheckoutStepType step_type, 304 AutocheckoutStepStatus step_status) { 305 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 306 NOTIMPLEMENTED() << " step_type=" << step_type 307 << " step_status=" << step_status; 308 } 309 310 void AutofillDialogControllerAndroid::OnAutocheckoutError() { 311 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 312 NOTIMPLEMENTED(); 313 DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_); 314 GetMetricLogger().LogAutocheckoutDuration( 315 base::Time::Now() - autocheckout_started_timestamp_, 316 AutofillMetrics::AUTOCHECKOUT_FAILED); 317 SetAutocheckoutState(AUTOCHECKOUT_ERROR); 318 autocheckout_started_timestamp_ = base::Time(); 319 } 320 321 void AutofillDialogControllerAndroid::OnAutocheckoutSuccess() { 322 // TODO(aruslan): http://crbug.com/177373 Autocheckout. 323 NOTIMPLEMENTED(); 324 DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_); 325 GetMetricLogger().LogAutocheckoutDuration( 326 base::Time::Now() - autocheckout_started_timestamp_, 327 AutofillMetrics::AUTOCHECKOUT_SUCCEEDED); 328 SetAutocheckoutState(AUTOCHECKOUT_SUCCESS); 329 autocheckout_started_timestamp_ = base::Time(); 330 } 331 332 DialogType AutofillDialogControllerAndroid::GetDialogType() const { 333 return dialog_type_; 334 } 335 336 // static 337 bool AutofillDialogControllerAndroid:: 338 RegisterAutofillDialogControllerAndroid(JNIEnv* env) { 339 return RegisterNativesImpl(env); 340 } 341 342 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env, 343 jobject obj) { 344 if (autocheckout_state_ == AUTOCHECKOUT_NOT_STARTED) 345 LogOnCancelMetrics(); 346 347 if (autocheckout_state_ == AUTOCHECKOUT_IN_PROGRESS) { 348 GetMetricLogger().LogAutocheckoutDuration( 349 base::Time::Now() - autocheckout_started_timestamp_, 350 AutofillMetrics::AUTOCHECKOUT_CANCELLED); 351 } 352 353 callback_.Run(NULL, std::string()); 354 } 355 356 void AutofillDialogControllerAndroid::DialogContinue( 357 JNIEnv* env, 358 jobject obj, 359 jobject wallet, 360 jboolean jlast_used_choice_is_autofill, 361 jstring jlast_used_account_name, 362 jstring jlast_used_billing, 363 jstring jlast_used_shipping, 364 jstring jlast_used_card) { 365 const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet); 366 const std::string google_transaction_id = 367 AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet); 368 369 const string16 last_used_account_name = 370 base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name); 371 const std::string last_used_billing = 372 base::android::ConvertJavaStringToUTF8(env, jlast_used_billing); 373 const std::string last_used_shipping = 374 base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping); 375 const std::string last_used_card = 376 base::android::ConvertJavaStringToUTF8(env, jlast_used_card); 377 378 scoped_ptr<wallet::FullWallet> full_wallet = 379 AutofillDialogResult::ConvertFromJava(env, wallet); 380 FillOutputForSection( 381 SECTION_EMAIL, form_structure_, full_wallet.get(), email); 382 FillOutputForSection( 383 SECTION_CC_BILLING, form_structure_, full_wallet.get(), email); 384 FillOutputForSection( 385 SECTION_SHIPPING, form_structure_, full_wallet.get(), email); 386 387 { 388 DictionaryPrefUpdate updater(profile_->GetPrefs(), 389 ::prefs::kAutofillDialogDefaults); 390 base::DictionaryValue* defaults = updater.Get(); 391 if (defaults) { 392 const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill; 393 defaults->SetString(kLastUsedAccountName, last_used_account_name); 394 defaults->SetBoolean(kLastUsedChoiceIsAutofill, 395 last_used_choice_is_autofill); 396 if (!last_used_billing.empty()) 397 defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing); 398 if (!last_used_shipping.empty()) 399 defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping); 400 if (!last_used_card.empty()) 401 defaults->SetString(kLastUsedCreditCardGuid, last_used_card); 402 } else { 403 LOG(ERROR) << "Failed to save AutofillDialog preferences"; 404 } 405 } 406 407 if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT) { 408 autocheckout_started_timestamp_ = base::Time::Now(); 409 SetAutocheckoutState(AUTOCHECKOUT_IN_PROGRESS); 410 } 411 412 LogOnFinishSubmitMetrics(); 413 414 // Callback should be called as late as possible. 415 callback_.Run(&form_structure_, google_transaction_id); 416 417 // This might delete us. 418 if (GetDialogType() == DIALOG_TYPE_REQUEST_AUTOCOMPLETE) 419 Hide(); 420 } 421 422 AutofillDialogControllerAndroid::AutofillDialogControllerAndroid( 423 content::WebContents* contents, 424 const FormData& form_structure, 425 const GURL& source_url, 426 const DialogType dialog_type, 427 const base::Callback<void(const FormStructure*, 428 const std::string&)>& callback) 429 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), 430 contents_(contents), 431 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN), 432 dialog_type_(dialog_type), 433 form_structure_(form_structure, std::string()), 434 invoked_from_same_origin_(true), 435 source_url_(source_url), 436 callback_(callback), 437 cares_about_shipping_(true), 438 weak_ptr_factory_(this), 439 autocheckout_state_(AUTOCHECKOUT_NOT_STARTED), 440 was_ui_latency_logged_(false) { 441 DCHECK(!callback_.is_null()); 442 } 443 444 bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const { 445 DCHECK_GT(form_structure_.field_count(), 0U); 446 447 for (size_t i = 0; i < form_structure_.field_count(); ++i) { 448 AutofillType type = form_structure_.field(i)->Type(); 449 if (common::IsCreditCardType(type.GetStorableType())) 450 return true; 451 } 452 453 return false; 454 } 455 456 bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const { 457 return source_url_.SchemeIs(chrome::kHttpsScheme); 458 } 459 460 void AutofillDialogControllerAndroid::SetAutocheckoutState( 461 AutocheckoutState autocheckout_state) { 462 if (autocheckout_state_ == autocheckout_state) 463 return; 464 465 autocheckout_state_ = autocheckout_state; 466 } 467 468 void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() { 469 GetMetricLogger().LogDialogUiDuration( 470 base::Time::Now() - dialog_shown_timestamp_, 471 GetDialogType(), 472 AutofillMetrics::DIALOG_ACCEPTED); 473 474 GetMetricLogger().LogDialogUiEvent( 475 GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED); 476 } 477 478 void AutofillDialogControllerAndroid::LogOnCancelMetrics() { 479 GetMetricLogger().LogDialogUiDuration( 480 base::Time::Now() - dialog_shown_timestamp_, 481 GetDialogType(), 482 AutofillMetrics::DIALOG_CANCELED); 483 484 GetMetricLogger().LogDialogUiEvent( 485 GetDialogType(), AutofillMetrics::DIALOG_UI_CANCELED); 486 } 487 488 } // namespace autofill 489