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_CC_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_CC_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(
124 section
== SECTION_CC_BILLING
125 ? full_wallet
->billing_address()->language_code()
126 : full_wallet
->shipping_address()->language_code(),
127 g_browser_process
->GetApplicationLocale());
130 void FillOutputForSection(
131 DialogSection section
,
132 FormStructure
& form_structure
,
133 FullWallet
* full_wallet
,
134 const base::string16
& email_address
) {
135 DCHECK(section
== SECTION_CC_BILLING
|| section
== SECTION_SHIPPING
);
137 if (section
== SECTION_CC_BILLING
)
138 BuildCcBillingInputs(&inputs
);
140 BuildShippingInputs(&inputs
);
142 FillOutputForSectionWithComparator(
143 section
, inputs
, base::Bind(ServerTypeMatchesField
, section
),
144 form_structure
, full_wallet
, email_address
);
146 if (section
== SECTION_CC_BILLING
) {
147 // Email is hidden while using Wallet, special case it.
148 for (size_t i
= 0; i
< form_structure
.field_count(); ++i
) {
149 AutofillField
* field
= form_structure
.field(i
);
150 if (field
->Type().GetStorableType() == EMAIL_ADDRESS
)
151 field
->value
= email_address
;
156 // Returns true if |input_type| in |section| is needed for |form_structure|.
157 bool IsSectionInputUsedInFormStructure(DialogSection section
,
158 ServerFieldType input_type
,
159 const FormStructure
& form_structure
) {
160 for (size_t i
= 0; i
< form_structure
.field_count(); ++i
) {
161 const AutofillField
* field
= form_structure
.field(i
);
162 if (field
&& ServerTypeMatchesField(section
, input_type
, *field
))
168 // Returns true if one of |inputs| in |section| is needed for |form_structure|.
169 bool IsSectionInputsUsedInFormStructure(DialogSection section
,
170 const ServerFieldType
* input_types
,
171 const size_t input_types_size
,
172 const FormStructure
& form_structure
) {
173 for (size_t i
= 0; i
< input_types_size
; ++i
) {
174 if (IsSectionInputUsedInFormStructure(
175 section
, input_types
[i
], form_structure
)) {
186 base::WeakPtr
<AutofillDialogController
> AutofillDialogControllerAndroid::Create(
187 content::WebContents
* contents
,
188 const FormData
& form_structure
,
189 const GURL
& source_url
,
190 const AutofillClient::ResultCallback
& callback
) {
191 // AutofillDialogControllerAndroid owns itself.
192 AutofillDialogControllerAndroid
* autofill_dialog_controller
=
193 new AutofillDialogControllerAndroid(contents
,
197 return autofill_dialog_controller
->weak_ptr_factory_
.GetWeakPtr();
200 #if defined(ENABLE_AUTOFILL_DIALOG)
202 base::WeakPtr
<AutofillDialogController
>
203 AutofillDialogController::Create(
204 content::WebContents
* contents
,
205 const FormData
& form_structure
,
206 const GURL
& source_url
,
207 const AutofillClient::ResultCallback
& callback
) {
208 return AutofillDialogControllerAndroid::Create(contents
,
215 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple
* registry
) {}
218 void AutofillDialogController::RegisterProfilePrefs(
219 user_prefs::PrefRegistrySyncable
* registry
) {
220 registry
->RegisterDictionaryPref(
221 ::prefs::kAutofillDialogDefaults
,
222 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
224 #endif // defined(ENABLE_AUTOFILL_DIALOG)
226 AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
227 if (java_object_
.is_null())
230 JNIEnv
* env
= base::android::AttachCurrentThread();
231 Java_AutofillDialogControllerAndroid_onDestroy(env
, java_object_
.obj());
234 void AutofillDialogControllerAndroid::Show() {
235 JNIEnv
* env
= base::android::AttachCurrentThread();
236 dialog_shown_timestamp_
= base::Time::Now();
238 // The Autofill dialog is shown in response to a message from the renderer and
239 // as such, it can only be made in the context of the current document. A call
240 // to GetActiveEntry would return a pending entry, if there was one, which
241 // would be a security bug. Therefore, we use the last committed URL for the
243 const GURL
& current_url
= contents_
->GetLastCommittedURL();
244 invoked_from_same_origin_
=
245 current_url
.GetOrigin() == source_url_
.GetOrigin();
247 // Fail if the dialog factory (e.g. SDK) doesn't support cross-origin calls.
248 if (!Java_AutofillDialogControllerAndroid_isDialogAllowed(
250 invoked_from_same_origin_
)) {
252 AutofillClient::AutocompleteResultErrorDisabled
,
253 base::ASCIIToUTF16("Cross-origin form invocations are not supported."),
259 // Determine what field types should be included in the dialog.
260 bool has_types
= false;
261 bool has_sections
= false;
262 form_structure_
.ParseFieldTypesFromAutocompleteAttributes(
263 &has_types
, &has_sections
);
265 // Fail if the author didn't specify autocomplete types, or
266 // if the dialog shouldn't be shown in a given circumstances.
269 AutofillClient::AutocompleteResultErrorDisabled
,
270 base::ASCIIToUTF16("Form is missing autocomplete attributes."),
276 // Fail if the author didn't ask for at least some kind of credit card
278 bool has_credit_card_field
= false;
279 for (size_t i
= 0; i
< form_structure_
.field_count(); ++i
) {
280 AutofillType type
= form_structure_
.field(i
)->Type();
281 if (type
.html_type() != HTML_TYPE_UNKNOWN
&& type
.group() == CREDIT_CARD
) {
282 has_credit_card_field
= true;
287 if (!has_credit_card_field
) {
289 AutofillClient::AutocompleteResultErrorDisabled
,
290 base::ASCIIToUTF16("Form is not a payment form (must contain "
291 "some autocomplete=\"cc-*\" fields). "),
297 // Log any relevant UI metrics and security exceptions.
298 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN
);
300 AutofillMetrics::LogDialogSecurityMetric(
301 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN
);
303 if (!invoked_from_same_origin_
) {
304 AutofillMetrics::LogDialogSecurityMetric(
305 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME
);
308 const ServerFieldType full_billing_is_necessary_if
[] = {
309 ADDRESS_BILLING_LINE1
,
310 ADDRESS_BILLING_LINE2
,
311 ADDRESS_BILLING_APT_NUM
,
312 ADDRESS_BILLING_CITY
,
313 ADDRESS_BILLING_STATE
,
314 // ADDRESS_BILLING_ZIP, // Postal code alone is a short form.
315 ADDRESS_BILLING_COUNTRY
,
316 ADDRESS_BILLING_STREET_ADDRESS
,
317 ADDRESS_BILLING_DEPENDENT_LOCALITY
,
318 ADDRESS_BILLING_SORTING_CODE
,
319 PHONE_BILLING_WHOLE_NUMBER
321 const ServerFieldType billing_phone_number_is_necessary_if
[] = {
322 PHONE_BILLING_WHOLE_NUMBER
324 const ServerFieldType shipping_phone_number_is_necessary_if
[] = {
325 PHONE_HOME_WHOLE_NUMBER
327 const bool request_full_billing_address
=
328 IsSectionInputsUsedInFormStructure(
330 full_billing_is_necessary_if
,
331 arraysize(full_billing_is_necessary_if
),
333 const bool request_phone_numbers
=
334 IsSectionInputsUsedInFormStructure(
336 billing_phone_number_is_necessary_if
,
337 arraysize(billing_phone_number_is_necessary_if
),
339 IsSectionInputsUsedInFormStructure(
341 shipping_phone_number_is_necessary_if
,
342 arraysize(shipping_phone_number_is_necessary_if
),
345 bool request_shipping_address
= false;
348 BuildShippingInputs(&inputs
);
349 request_shipping_address
= form_structure_
.FillFields(
350 TypesFromInputs(inputs
),
351 base::Bind(ServerTypeMatchesField
, SECTION_SHIPPING
),
352 base::Bind(NullGetInfo
), std::string(),
353 g_browser_process
->GetApplicationLocale());
356 bool last_used_choice_is_autofill
= false;
357 base::string16 last_used_account_name
;
358 std::string last_used_billing
;
359 std::string last_used_shipping
;
360 std::string last_used_credit_card
;
362 const base::DictionaryValue
* defaults
=
363 profile_
->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults
);
365 defaults
->GetString(kLastUsedAccountName
, &last_used_account_name
);
366 defaults
->GetBoolean(kLastUsedChoiceIsAutofill
,
367 &last_used_choice_is_autofill
);
368 defaults
->GetString(kLastUsedBillingAddressGuid
, &last_used_billing
);
369 defaults
->GetString(kLastUsedShippingAddressGuid
, &last_used_shipping
);
370 defaults
->GetString(kLastUsedCreditCardGuid
, &last_used_credit_card
);
372 DLOG(ERROR
) << "Failed to read AutofillDialog preferences";
376 const bool incognito_mode
= profile_
->IsOffTheRecord();
378 last_used_choice_is_autofill
= true;
380 ScopedJavaLocalRef
<jstring
> jlast_used_account_name
=
381 base::android::ConvertUTF16ToJavaString(
382 env
, last_used_account_name
);
383 ScopedJavaLocalRef
<jstring
> jlast_used_billing
=
384 base::android::ConvertUTF8ToJavaString(
385 env
, last_used_billing
);
386 ScopedJavaLocalRef
<jstring
> jlast_used_shipping
=
387 base::android::ConvertUTF8ToJavaString(
388 env
, last_used_shipping
);
389 ScopedJavaLocalRef
<jstring
> jlast_used_card
=
390 base::android::ConvertUTF8ToJavaString(
391 env
, last_used_credit_card
);
392 ScopedJavaLocalRef
<jstring
> jmerchant_domain
=
393 base::android::ConvertUTF8ToJavaString(
394 env
, source_url_
.GetOrigin().spec());
395 const std::set
<base::string16
> available_shipping_countries
=
396 form_structure_
.PossibleValues(ADDRESS_HOME_COUNTRY
);
397 ScopedJavaLocalRef
<jobjectArray
> jshipping_countries
=
398 base::android::ToJavaArrayOfStrings(
400 std::vector
<base::string16
>(available_shipping_countries
.begin(),
401 available_shipping_countries
.end()));
402 const std::set
<base::string16
> available_credit_card_types
=
403 form_structure_
.PossibleValues(CREDIT_CARD_TYPE
);
404 ScopedJavaLocalRef
<jobjectArray
> jcredit_card_types
=
405 base::android::ToJavaArrayOfStrings(
407 std::vector
<base::string16
>(available_credit_card_types
.begin(),
408 available_credit_card_types
.end()));
410 java_object_
.Reset(Java_AutofillDialogControllerAndroid_create(
412 reinterpret_cast<intptr_t>(this),
413 WindowAndroidHelper::FromWebContents(contents_
)->
414 GetWindowAndroid()->GetJavaObject().obj(),
415 request_full_billing_address
, request_shipping_address
,
416 request_phone_numbers
, incognito_mode
,
417 last_used_choice_is_autofill
, jlast_used_account_name
.obj(),
418 jlast_used_billing
.obj(), jlast_used_shipping
.obj(),
419 jlast_used_card
.obj(),
420 jmerchant_domain
.obj(),
421 jshipping_countries
.obj(),
422 jcredit_card_types
.obj()));
425 void AutofillDialogControllerAndroid::Hide() {
429 void AutofillDialogControllerAndroid::TabActivated() {}
432 bool AutofillDialogControllerAndroid::
433 RegisterAutofillDialogControllerAndroid(JNIEnv
* env
) {
434 return RegisterNativesImpl(env
);
437 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv
* env
,
439 LogOnCancelMetrics();
440 callback_
.Run(AutofillClient::AutocompleteResultErrorCancel
,
445 void AutofillDialogControllerAndroid::DialogContinue(
449 jboolean jlast_used_choice_is_autofill
,
450 jstring jlast_used_account_name
,
451 jstring jlast_used_billing
,
452 jstring jlast_used_shipping
,
453 jstring jlast_used_card
) {
454 const base::string16 email
=
455 AutofillDialogResult::GetWalletEmail(env
, wallet
);
456 const std::string google_transaction_id
=
457 AutofillDialogResult::GetWalletGoogleTransactionId(env
, wallet
);
459 const base::string16 last_used_account_name
=
460 base::android::ConvertJavaStringToUTF16(env
, jlast_used_account_name
);
461 const std::string last_used_billing
=
462 base::android::ConvertJavaStringToUTF8(env
, jlast_used_billing
);
463 const std::string last_used_shipping
=
464 base::android::ConvertJavaStringToUTF8(env
, jlast_used_shipping
);
465 const std::string last_used_card
=
466 base::android::ConvertJavaStringToUTF8(env
, jlast_used_card
);
468 scoped_ptr
<FullWallet
> full_wallet
=
469 AutofillDialogResult::ConvertFromJava(env
, wallet
);
470 FillOutputForSection(
471 SECTION_CC_BILLING
, form_structure_
, full_wallet
.get(), email
);
472 FillOutputForSection(
473 SECTION_SHIPPING
, form_structure_
, full_wallet
.get(), email
);
476 DictionaryPrefUpdate
updater(profile_
->GetPrefs(),
477 ::prefs::kAutofillDialogDefaults
);
478 base::DictionaryValue
* defaults
= updater
.Get();
480 const bool last_used_choice_is_autofill
= !!jlast_used_choice_is_autofill
;
481 defaults
->SetString(kLastUsedAccountName
, last_used_account_name
);
482 defaults
->SetBoolean(kLastUsedChoiceIsAutofill
,
483 last_used_choice_is_autofill
);
484 if (!last_used_billing
.empty())
485 defaults
->SetString(kLastUsedBillingAddressGuid
, last_used_billing
);
486 if (!last_used_shipping
.empty())
487 defaults
->SetString(kLastUsedShippingAddressGuid
, last_used_shipping
);
488 if (!last_used_card
.empty())
489 defaults
->SetString(kLastUsedCreditCardGuid
, last_used_card
);
491 DLOG(ERROR
) << "Failed to save AutofillDialog preferences";
495 LogOnFinishSubmitMetrics();
497 // Callback should be called as late as possible.
498 callback_
.Run(AutofillClient::AutocompleteResultSuccess
,
502 // This might delete us.
506 AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
507 content::WebContents
* contents
,
508 const FormData
& form_structure
,
509 const GURL
& source_url
,
510 const AutofillClient::ResultCallback
& callback
)
511 : profile_(Profile::FromBrowserContext(contents
->GetBrowserContext())),
513 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN
),
514 form_structure_(form_structure
),
515 invoked_from_same_origin_(true),
516 source_url_(source_url
),
518 cares_about_shipping_(true),
519 was_ui_latency_logged_(false),
520 weak_ptr_factory_(this) {
521 DCHECK(!callback_
.is_null());
524 void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
525 AutofillMetrics::LogDialogUiDuration(
526 base::Time::Now() - dialog_shown_timestamp_
,
527 AutofillMetrics::DIALOG_ACCEPTED
);
529 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED
);
532 void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
533 AutofillMetrics::LogDialogUiDuration(
534 base::Time::Now() - dialog_shown_timestamp_
,
535 AutofillMetrics::DIALOG_CANCELED
);
537 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED
);
540 } // namespace autofill