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/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/base/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"
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";
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
>();
65 return scoped_ptr
<DataModelWrapper
>(
66 new FullWalletBillingWrapper(full_wallet
));
68 if (section
== SECTION_SHIPPING
) {
69 if (!full_wallet
->shipping_address())
70 return scoped_ptr
<DataModelWrapper
>();
72 return scoped_ptr
<DataModelWrapper
>(
73 new FullWalletShippingWrapper(full_wallet
));
76 return scoped_ptr
<DataModelWrapper
>();
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 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
, full_wallet
);
86 wrapper
->FillFormStructure(inputs
, compare
, &form_structure
);
89 void FillOutputForSection(
90 DialogSection section
,
91 FormStructure
& form_structure
,
92 wallet::FullWallet
* full_wallet
,
93 const base::string16
& email_address
) {
95 common::BuildInputsForSection(section
, "US", &inputs
);
97 FillOutputForSectionWithComparator(
99 base::Bind(common::DetailInputMatchesField
, section
),
100 form_structure
, full_wallet
, email_address
);
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
;
112 // Returns true if |input_type| in |section| is needed for |form_structure|.
113 bool IsSectionInputUsedInFormStructure(DialogSection section
,
114 ServerFieldType input_type
,
115 const FormStructure
& form_structure
) {
116 const DetailInput input
= { DetailInput::LONG
, 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
))
125 // Returns true if one of |inputs| in |section| is needed for |form_structure|.
126 bool 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
)) {
143 base::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
*)>& callback
) {
148 // AutofillDialogControllerAndroid owns itself.
149 AutofillDialogControllerAndroid
* autofill_dialog_controller
=
150 new AutofillDialogControllerAndroid(contents
,
154 return autofill_dialog_controller
->weak_ptr_factory_
.GetWeakPtr();
157 #if defined(ENABLE_AUTOFILL_DIALOG)
159 base::WeakPtr
<AutofillDialogController
>
160 AutofillDialogController::Create(
161 content::WebContents
* contents
,
162 const FormData
& form_structure
,
163 const GURL
& source_url
,
164 const base::Callback
<void(const FormStructure
*)>& callback
) {
165 return AutofillDialogControllerAndroid::Create(contents
,
172 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple
* registry
) {}
175 void AutofillDialogController::RegisterProfilePrefs(
176 user_prefs::PrefRegistrySyncable
* registry
) {
177 registry
->RegisterDictionaryPref(
178 ::prefs::kAutofillDialogDefaults
,
179 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
181 #endif // defined(ENABLE_AUTOFILL_DIALOG)
183 AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
184 if (java_object_
.is_null())
187 JNIEnv
* env
= base::android::AttachCurrentThread();
188 Java_AutofillDialogControllerAndroid_onDestroy(env
, java_object_
.obj());
191 void AutofillDialogControllerAndroid::Show() {
192 JNIEnv
* env
= base::android::AttachCurrentThread();
193 dialog_shown_timestamp_
= base::Time::Now();
195 const GURL
& current_url
= contents_
->GetLastCommittedURL();
196 invoked_from_same_origin_
=
197 current_url
.GetOrigin() == source_url_
.GetOrigin();
199 // Determine what field types should be included in the dialog.
200 bool has_types
= false;
201 bool has_sections
= false;
202 form_structure_
.ParseFieldTypesFromAutocompleteAttributes(
203 &has_types
, &has_sections
);
205 // Fail if the author didn't specify autocomplete types, or
206 // if the dialog shouldn't be shown in a given circumstances.
208 !Java_AutofillDialogControllerAndroid_isDialogAllowed(
210 invoked_from_same_origin_
)) {
216 // Log any relevant UI metrics and security exceptions.
217 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN
);
219 GetMetricLogger().LogDialogSecurityMetric(
220 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN
);
222 if (!invoked_from_same_origin_
) {
223 GetMetricLogger().LogDialogSecurityMetric(
224 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME
);
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
234 const bool request_full_billing_address
=
235 IsSectionInputsUsedInFormStructure(
237 full_billing_is_necessary_if
,
238 arraysize(full_billing_is_necessary_if
),
240 const bool request_phone_numbers
=
241 IsSectionInputUsedInFormStructure(
243 PHONE_BILLING_WHOLE_NUMBER
,
245 IsSectionInputUsedInFormStructure(
247 PHONE_HOME_WHOLE_NUMBER
,
250 bool request_shipping_address
= false;
253 common::BuildInputsForSection(SECTION_SHIPPING
, "US", &inputs
);
254 EmptyDataModelWrapper empty_wrapper
;
255 request_shipping_address
= empty_wrapper
.FillFormStructure(
257 base::Bind(common::DetailInputMatchesField
, SECTION_SHIPPING
),
261 const bool incognito_mode
= profile_
->IsOffTheRecord();
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
;
269 const base::DictionaryValue
* defaults
=
270 profile_
->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults
);
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
);
279 DLOG(ERROR
) << "Failed to read AutofillDialog preferences";
283 if (contents_
->GetBrowserContext()->IsOffTheRecord())
284 last_used_choice_is_autofill
= true;
286 ScopedJavaLocalRef
<jstring
> jlast_used_account_name
=
287 base::android::ConvertUTF16ToJavaString(
288 env
, last_used_account_name
);
289 ScopedJavaLocalRef
<jstring
> jlast_used_billing
=
290 base::android::ConvertUTF8ToJavaString(
291 env
, last_used_billing
);
292 ScopedJavaLocalRef
<jstring
> jlast_used_shipping
=
293 base::android::ConvertUTF8ToJavaString(
294 env
, last_used_shipping
);
295 ScopedJavaLocalRef
<jstring
> jlast_used_card
=
296 base::android::ConvertUTF8ToJavaString(
297 env
, last_used_credit_card
);
298 ScopedJavaLocalRef
<jstring
> jmerchant_domain
=
299 base::android::ConvertUTF8ToJavaString(
300 env
, source_url_
.GetOrigin().spec());
301 java_object_
.Reset(Java_AutofillDialogControllerAndroid_create(
303 reinterpret_cast<intptr_t>(this),
304 WindowAndroidHelper::FromWebContents(contents_
)->
305 GetWindowAndroid()->GetJavaObject().obj(),
306 request_full_billing_address
, request_shipping_address
,
307 request_phone_numbers
, incognito_mode
,
308 last_used_choice_is_autofill
, jlast_used_account_name
.obj(),
309 jlast_used_billing
.obj(), jlast_used_shipping
.obj(),
310 jlast_used_card
.obj(),
311 jmerchant_domain
.obj()));
314 void AutofillDialogControllerAndroid::Hide() {
318 void AutofillDialogControllerAndroid::TabActivated() {}
321 bool AutofillDialogControllerAndroid::
322 RegisterAutofillDialogControllerAndroid(JNIEnv
* env
) {
323 return RegisterNativesImpl(env
);
326 void AutofillDialogControllerAndroid::DialogCancel(JNIEnv
* env
,
328 LogOnCancelMetrics();
332 void AutofillDialogControllerAndroid::DialogContinue(
336 jboolean jlast_used_choice_is_autofill
,
337 jstring jlast_used_account_name
,
338 jstring jlast_used_billing
,
339 jstring jlast_used_shipping
,
340 jstring jlast_used_card
) {
341 const base::string16 email
=
342 AutofillDialogResult::GetWalletEmail(env
, wallet
);
343 const std::string google_transaction_id
=
344 AutofillDialogResult::GetWalletGoogleTransactionId(env
, wallet
);
346 const base::string16 last_used_account_name
=
347 base::android::ConvertJavaStringToUTF16(env
, jlast_used_account_name
);
348 const std::string last_used_billing
=
349 base::android::ConvertJavaStringToUTF8(env
, jlast_used_billing
);
350 const std::string last_used_shipping
=
351 base::android::ConvertJavaStringToUTF8(env
, jlast_used_shipping
);
352 const std::string last_used_card
=
353 base::android::ConvertJavaStringToUTF8(env
, jlast_used_card
);
355 scoped_ptr
<wallet::FullWallet
> full_wallet
=
356 AutofillDialogResult::ConvertFromJava(env
, wallet
);
357 FillOutputForSection(
358 SECTION_CC_BILLING
, form_structure_
, full_wallet
.get(), email
);
359 FillOutputForSection(
360 SECTION_SHIPPING
, form_structure_
, full_wallet
.get(), email
);
363 DictionaryPrefUpdate
updater(profile_
->GetPrefs(),
364 ::prefs::kAutofillDialogDefaults
);
365 base::DictionaryValue
* defaults
= updater
.Get();
367 const bool last_used_choice_is_autofill
= !!jlast_used_choice_is_autofill
;
368 defaults
->SetString(kLastUsedAccountName
, last_used_account_name
);
369 defaults
->SetBoolean(kLastUsedChoiceIsAutofill
,
370 last_used_choice_is_autofill
);
371 if (!last_used_billing
.empty())
372 defaults
->SetString(kLastUsedBillingAddressGuid
, last_used_billing
);
373 if (!last_used_shipping
.empty())
374 defaults
->SetString(kLastUsedShippingAddressGuid
, last_used_shipping
);
375 if (!last_used_card
.empty())
376 defaults
->SetString(kLastUsedCreditCardGuid
, last_used_card
);
378 LOG(ERROR
) << "Failed to save AutofillDialog preferences";
382 LogOnFinishSubmitMetrics();
384 // Callback should be called as late as possible.
385 callback_
.Run(&form_structure_
);
387 // This might delete us.
391 AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
392 content::WebContents
* contents
,
393 const FormData
& form_structure
,
394 const GURL
& source_url
,
395 const base::Callback
<void(const FormStructure
*)>& callback
)
396 : profile_(Profile::FromBrowserContext(contents
->GetBrowserContext())),
398 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN
),
399 form_structure_(form_structure
),
400 invoked_from_same_origin_(true),
401 source_url_(source_url
),
403 cares_about_shipping_(true),
404 weak_ptr_factory_(this),
405 was_ui_latency_logged_(false) {
406 DCHECK(!callback_
.is_null());
409 void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
410 GetMetricLogger().LogDialogUiDuration(
411 base::Time::Now() - dialog_shown_timestamp_
,
412 AutofillMetrics::DIALOG_ACCEPTED
);
414 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED
);
417 void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
418 GetMetricLogger().LogDialogUiDuration(
419 base::Time::Now() - dialog_shown_timestamp_
,
420 AutofillMetrics::DIALOG_CANCELED
);
422 GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED
);
425 } // namespace autofill