Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / android / autofill / autofill_dialog_controller_android.cc
blob160579a966845548ea65c05d7a8d84e02743ff98
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.
5 #include "chrome/browser/ui/android/autofill/autofill_dialog_controller_android.h"
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/detail_input.h"
31 #include "components/autofill/core/browser/dialog_section.h"
32 #include "components/autofill/core/browser/field_types.h"
33 #include "components/autofill/core/browser/personal_data_manager.h"
34 #include "components/autofill/core/browser/server_field_types_util.h"
35 #include "components/autofill/core/common/form_data.h"
36 #include "components/pref_registry/pref_registry_syncable.h"
37 #include "content/public/browser/navigation_controller.h"
38 #include "content/public/browser/navigation_details.h"
39 #include "content/public/browser/navigation_entry.h"
40 #include "content/public/browser/web_contents.h"
41 #include "jni/AutofillDialogControllerAndroid_jni.h"
42 #include "ui/android/window_android.h"
43 #include "ui/base/models/combobox_model.h"
44 #include "ui/base/models/menu_model.h"
45 #include "ui/gfx/android/java_bitmap.h"
46 #include "ui/gfx/geometry/rect.h"
47 #include "url/gurl.h"
49 namespace autofill {
51 namespace {
53 using wallet::FullWallet;
55 // Keys in kAutofillDialogDefaults pref dictionary (do not change these values).
56 const char kLastUsedAccountName[] = "last_used_account_name";
57 const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill";
58 const char kLastUsedBillingAddressGuid[] = "last_used_billing";
59 const char kLastUsedShippingAddressGuid[] = "last_used_shipping";
60 const char kLastUsedCreditCardGuid[] = "last_used_card";
62 // Constructs |inputs| for the SECTION_BILLING section.
63 void BuildCcBillingInputs(DetailInputs* inputs) {
64 const DetailInput kCcBillingInputs[] = {
65 { DetailInput::LONG, NAME_BILLING_FULL },
66 { DetailInput::LONG, ADDRESS_BILLING_STREET_ADDRESS },
67 { DetailInput::LONG, ADDRESS_BILLING_CITY },
68 { DetailInput::LONG, ADDRESS_BILLING_DEPENDENT_LOCALITY },
69 { DetailInput::LONG, ADDRESS_BILLING_STATE },
70 { DetailInput::LONG, ADDRESS_BILLING_ZIP },
71 { DetailInput::LONG, ADDRESS_BILLING_SORTING_CODE },
72 { DetailInput::LONG, ADDRESS_BILLING_COUNTRY },
73 { DetailInput::LONG, PHONE_BILLING_WHOLE_NUMBER },
74 { DetailInput::LONG, CREDIT_CARD_NUMBER },
75 { DetailInput::LONG, CREDIT_CARD_EXP_MONTH },
76 { DetailInput::LONG, CREDIT_CARD_EXP_4_DIGIT_YEAR },
77 { DetailInput::LONG, CREDIT_CARD_VERIFICATION_CODE },
79 BuildInputs(kCcBillingInputs, arraysize(kCcBillingInputs), inputs);
82 // Constructs |inputs| for the SECTION_SHIPPING section.
83 void BuildShippingInputs(DetailInputs* inputs) {
84 const DetailInput kShippingInputs[] = {
85 { DetailInput::LONG, NAME_FULL },
86 { DetailInput::LONG, ADDRESS_HOME_STREET_ADDRESS },
87 { DetailInput::LONG, ADDRESS_HOME_CITY },
88 { DetailInput::LONG, ADDRESS_HOME_DEPENDENT_LOCALITY },
89 { DetailInput::LONG, ADDRESS_HOME_STATE },
90 { DetailInput::LONG, ADDRESS_HOME_ZIP },
91 { DetailInput::LONG, ADDRESS_HOME_SORTING_CODE },
92 { DetailInput::LONG, ADDRESS_HOME_COUNTRY },
93 { DetailInput::LONG, PHONE_HOME_WHOLE_NUMBER },
95 BuildInputs(kShippingInputs, arraysize(kShippingInputs), inputs);
98 base::string16 NullGetInfo(const AutofillType& type) {
99 return base::string16();
102 void FillOutputForSectionWithComparator(
103 DialogSection section,
104 const DetailInputs& inputs,
105 const FormStructure::InputFieldComparator& compare,
106 FormStructure& form_structure,
107 FullWallet* full_wallet,
108 const base::string16& email_address) {
109 if ((section == SECTION_BILLING && !full_wallet->billing_address()) ||
110 (section == SECTION_SHIPPING && !full_wallet->shipping_address())) {
111 return;
114 base::Callback<base::string16(const AutofillType&)> get_info =
115 base::Bind(&FullWallet::GetInfo,
116 base::Unretained(full_wallet),
117 g_browser_process->GetApplicationLocale());
119 std::vector<ServerFieldType> types = TypesFromInputs(inputs);
120 form_structure.FillFields(
121 types, compare, get_info,
122 section == SECTION_BILLING
123 ? full_wallet->billing_address()->language_code()
124 : full_wallet->shipping_address()->language_code(),
125 g_browser_process->GetApplicationLocale());
128 void FillOutputForSection(
129 DialogSection section,
130 FormStructure& form_structure,
131 FullWallet* full_wallet,
132 const base::string16& email_address) {
133 DetailInputs inputs;
134 if (section == SECTION_BILLING)
135 BuildCcBillingInputs(&inputs);
136 else
137 BuildShippingInputs(&inputs);
139 FillOutputForSectionWithComparator(
140 section, inputs, base::Bind(ServerTypeMatchesField, section),
141 form_structure, full_wallet, email_address);
143 if (section == SECTION_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;
153 // Returns true if |input_type| in |section| is needed for |form_structure|.
154 bool 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 && ServerTypeMatchesField(section, input_type, *field))
160 return true;
162 return false;
165 // Returns true if one of |inputs| in |section| is needed for |form_structure|.
166 bool 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;
176 return false;
179 } // namespace
182 // static
183 base::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();
197 #if defined(ENABLE_AUTOFILL_DIALOG)
198 // static
199 base::WeakPtr<AutofillDialogController>
200 AutofillDialogController::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);
211 // static
212 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {}
214 // static
215 void AutofillDialogController::RegisterProfilePrefs(
216 user_prefs::PrefRegistrySyncable* registry) {
217 registry->RegisterDictionaryPref(
218 ::prefs::kAutofillDialogDefaults,
219 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
221 #endif // defined(ENABLE_AUTOFILL_DIALOG)
223 AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
224 if (java_object_.is_null())
225 return;
227 JNIEnv* env = base::android::AttachCurrentThread();
228 Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
231 void AutofillDialogControllerAndroid::Show() {
232 JNIEnv* env = base::android::AttachCurrentThread();
233 dialog_shown_timestamp_ = base::Time::Now();
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();
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;
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);
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;
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;
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;
294 // Log any relevant UI metrics and security exceptions.
295 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
297 AutofillMetrics::LogDialogSecurityMetric(
298 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
300 if (!invoked_from_same_origin_) {
301 AutofillMetrics::LogDialogSecurityMetric(
302 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
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
318 const ServerFieldType billing_phone_number_is_necessary_if[] = {
319 PHONE_BILLING_WHOLE_NUMBER
321 const ServerFieldType shipping_phone_number_is_necessary_if[] = {
322 PHONE_HOME_WHOLE_NUMBER
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_);
342 bool request_shipping_address = false;
344 DetailInputs inputs;
345 BuildShippingInputs(&inputs);
346 request_shipping_address = form_structure_.FillFields(
347 TypesFromInputs(inputs),
348 base::Bind(ServerTypeMatchesField, SECTION_SHIPPING),
349 base::Bind(NullGetInfo), std::string(),
350 g_browser_process->GetApplicationLocale());
353 bool last_used_choice_is_autofill = false;
354 base::string16 last_used_account_name;
355 std::string last_used_billing;
356 std::string last_used_shipping;
357 std::string last_used_credit_card;
359 const base::DictionaryValue* defaults =
360 profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
361 if (defaults) {
362 defaults->GetString(kLastUsedAccountName, &last_used_account_name);
363 defaults->GetBoolean(kLastUsedChoiceIsAutofill,
364 &last_used_choice_is_autofill);
365 defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
366 defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
367 defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
368 } else {
369 DLOG(ERROR) << "Failed to read AutofillDialog preferences";
373 const bool incognito_mode = profile_->IsOffTheRecord();
374 if (incognito_mode)
375 last_used_choice_is_autofill = true;
377 ScopedJavaLocalRef<jstring> jlast_used_account_name =
378 base::android::ConvertUTF16ToJavaString(
379 env, last_used_account_name);
380 ScopedJavaLocalRef<jstring> jlast_used_billing =
381 base::android::ConvertUTF8ToJavaString(
382 env, last_used_billing);
383 ScopedJavaLocalRef<jstring> jlast_used_shipping =
384 base::android::ConvertUTF8ToJavaString(
385 env, last_used_shipping);
386 ScopedJavaLocalRef<jstring> jlast_used_card =
387 base::android::ConvertUTF8ToJavaString(
388 env, last_used_credit_card);
389 ScopedJavaLocalRef<jstring> jmerchant_domain =
390 base::android::ConvertUTF8ToJavaString(
391 env, source_url_.GetOrigin().spec());
392 const std::set<base::string16> available_shipping_countries =
393 form_structure_.PossibleValues(ADDRESS_HOME_COUNTRY);
394 ScopedJavaLocalRef<jobjectArray> jshipping_countries =
395 base::android::ToJavaArrayOfStrings(
396 env,
397 std::vector<base::string16>(available_shipping_countries.begin(),
398 available_shipping_countries.end()));
399 const std::set<base::string16> available_credit_card_types =
400 form_structure_.PossibleValues(CREDIT_CARD_TYPE);
401 ScopedJavaLocalRef<jobjectArray> jcredit_card_types =
402 base::android::ToJavaArrayOfStrings(
403 env,
404 std::vector<base::string16>(available_credit_card_types.begin(),
405 available_credit_card_types.end()));
407 java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
408 env,
409 reinterpret_cast<intptr_t>(this),
410 WindowAndroidHelper::FromWebContents(contents_)->
411 GetWindowAndroid()->GetJavaObject().obj(),
412 request_full_billing_address, request_shipping_address,
413 request_phone_numbers, incognito_mode,
414 last_used_choice_is_autofill, jlast_used_account_name.obj(),
415 jlast_used_billing.obj(), jlast_used_shipping.obj(),
416 jlast_used_card.obj(),
417 jmerchant_domain.obj(),
418 jshipping_countries.obj(),
419 jcredit_card_types.obj()));
422 void AutofillDialogControllerAndroid::Hide() {
423 delete this;
426 void AutofillDialogControllerAndroid::TabActivated() {}
428 // static
429 bool AutofillDialogControllerAndroid::
430 RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
431 return RegisterNativesImpl(env);
434 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
435 jobject obj) {
436 LogOnCancelMetrics();
437 callback_.Run(AutofillClient::AutocompleteResultErrorCancel,
438 base::string16(),
439 NULL);
442 void AutofillDialogControllerAndroid::DialogContinue(
443 JNIEnv* env,
444 jobject obj,
445 jobject wallet,
446 jboolean jlast_used_choice_is_autofill,
447 jstring jlast_used_account_name,
448 jstring jlast_used_billing,
449 jstring jlast_used_shipping,
450 jstring jlast_used_card) {
451 const base::string16 email =
452 AutofillDialogResult::GetWalletEmail(env, wallet);
453 const std::string google_transaction_id =
454 AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
456 const base::string16 last_used_account_name =
457 base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
458 const std::string last_used_billing =
459 base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
460 const std::string last_used_shipping =
461 base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
462 const std::string last_used_card =
463 base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
465 scoped_ptr<FullWallet> full_wallet =
466 AutofillDialogResult::ConvertFromJava(env, wallet);
467 FillOutputForSection(SECTION_BILLING, form_structure_, full_wallet.get(),
468 email);
469 FillOutputForSection(
470 SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
473 DictionaryPrefUpdate updater(profile_->GetPrefs(),
474 ::prefs::kAutofillDialogDefaults);
475 base::DictionaryValue* defaults = updater.Get();
476 if (defaults) {
477 const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
478 defaults->SetString(kLastUsedAccountName, last_used_account_name);
479 defaults->SetBoolean(kLastUsedChoiceIsAutofill,
480 last_used_choice_is_autofill);
481 if (!last_used_billing.empty())
482 defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
483 if (!last_used_shipping.empty())
484 defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
485 if (!last_used_card.empty())
486 defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
487 } else {
488 DLOG(ERROR) << "Failed to save AutofillDialog preferences";
492 LogOnFinishSubmitMetrics();
494 // Callback should be called as late as possible.
495 callback_.Run(AutofillClient::AutocompleteResultSuccess,
496 base::string16(),
497 &form_structure_);
499 // This might delete us.
500 Hide();
503 AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
504 content::WebContents* contents,
505 const FormData& form_structure,
506 const GURL& source_url,
507 const AutofillClient::ResultCallback& callback)
508 : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
509 contents_(contents),
510 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
511 form_structure_(form_structure),
512 invoked_from_same_origin_(true),
513 source_url_(source_url),
514 callback_(callback),
515 cares_about_shipping_(true),
516 was_ui_latency_logged_(false),
517 weak_ptr_factory_(this) {
518 DCHECK(!callback_.is_null());
521 void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
522 AutofillMetrics::LogDialogUiDuration(
523 base::Time::Now() - dialog_shown_timestamp_,
524 AutofillMetrics::DIALOG_ACCEPTED);
526 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
529 void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
530 AutofillMetrics::LogDialogUiDuration(
531 base::Time::Now() - dialog_shown_timestamp_,
532 AutofillMetrics::DIALOG_CANCELED);
534 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
537 } // namespace autofill