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"
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())) {
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
) {
134 if (section
== SECTION_BILLING
)
135 BuildCcBillingInputs(&inputs
);
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
))
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
)) {
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
,
194 return autofill_dialog_controller
->weak_ptr_factory_
.GetWeakPtr();
197 #if defined(ENABLE_AUTOFILL_DIALOG)
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
,
212 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple
* registry
) {}
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())
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
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(
247 invoked_from_same_origin_
)) {
249 AutofillClient::AutocompleteResultErrorDisabled
,
250 base::ASCIIToUTF16("Cross-origin form invocations are not supported."),
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.
266 AutofillClient::AutocompleteResultErrorDisabled
,
267 base::ASCIIToUTF16("Form is missing autocomplete attributes."),
273 // Fail if the author didn't ask for at least some kind of credit card
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;
284 if (!has_credit_card_field
) {
286 AutofillClient::AutocompleteResultErrorDisabled
,
287 base::ASCIIToUTF16("Form is not a payment form (must contain "
288 "some autocomplete=\"cc-*\" fields). "),
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(
327 full_billing_is_necessary_if
,
328 arraysize(full_billing_is_necessary_if
),
330 const bool request_phone_numbers
=
331 IsSectionInputsUsedInFormStructure(
333 billing_phone_number_is_necessary_if
,
334 arraysize(billing_phone_number_is_necessary_if
),
336 IsSectionInputsUsedInFormStructure(
338 shipping_phone_number_is_necessary_if
,
339 arraysize(shipping_phone_number_is_necessary_if
),
342 bool request_shipping_address
= false;
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
);
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
);
369 DLOG(ERROR
) << "Failed to read AutofillDialog preferences";
373 const bool incognito_mode
= profile_
->IsOffTheRecord();
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(
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(
404 std::vector
<base::string16
>(available_credit_card_types
.begin(),
405 available_credit_card_types
.end()));
407 java_object_
.Reset(Java_AutofillDialogControllerAndroid_create(
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() {
426 void AutofillDialogControllerAndroid::TabActivated() {}
429 bool AutofillDialogControllerAndroid::
430 RegisterAutofillDialogControllerAndroid(JNIEnv
* env
) {
431 return RegisterNativesImpl(env
);
434 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv
* env
,
436 LogOnCancelMetrics();
437 callback_
.Run(AutofillClient::AutocompleteResultErrorCancel
,
442 void AutofillDialogControllerAndroid::DialogContinue(
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(),
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();
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
);
488 DLOG(ERROR
) << "Failed to save AutofillDialog preferences";
492 LogOnFinishSubmitMetrics();
494 // Callback should be called as late as possible.
495 callback_
.Run(AutofillClient::AutocompleteResultSuccess
,
499 // This might delete us.
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())),
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
),
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