autofill_dialog_controller_android.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/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/personal_data_manager.h"
31#include "components/autofill/core/common/form_data.h"
32#include "components/pref_registry/pref_registry_syncable.h"
33#include "content/public/browser/navigation_controller.h"
34#include "content/public/browser/navigation_details.h"
35#include "content/public/browser/navigation_entry.h"
36#include "content/public/browser/web_contents.h"
37#include "grit/generated_resources.h"
38#include "jni/AutofillDialogControllerAndroid_jni.h"
39#include "ui/base/android/window_android.h"
40#include "ui/base/l10n/l10n_util.h"
41#include "ui/base/models/combobox_model.h"
42#include "ui/base/models/menu_model.h"
43#include "ui/gfx/android/java_bitmap.h"
44#include "ui/gfx/rect.h"
45#include "url/gurl.h"
46
47namespace autofill {
48
49namespace {
50
51using wallet::FullWallet;
52
53// Keys in kAutofillDialogDefaults pref dictionary (do not change these values).
54const char kLastUsedAccountName[] = "last_used_account_name";
55const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill";
56const char kLastUsedBillingAddressGuid[] = "last_used_billing";
57const char kLastUsedShippingAddressGuid[] = "last_used_shipping";
58const char kLastUsedCreditCardGuid[] = "last_used_card";
59
60// Constructs |inputs| for the SECTION_CC_BILLING section.
61void BuildCcBillingInputs(DetailInputs* inputs) {
62  const DetailInput kCcBillingInputs[] = {
63    { DetailInput::LONG, NAME_BILLING_FULL },
64    { DetailInput::LONG, ADDRESS_BILLING_STREET_ADDRESS },
65    { DetailInput::LONG, ADDRESS_BILLING_CITY },
66    { DetailInput::LONG, ADDRESS_BILLING_DEPENDENT_LOCALITY },
67    { DetailInput::LONG, ADDRESS_BILLING_STATE },
68    { DetailInput::LONG, ADDRESS_BILLING_ZIP },
69    { DetailInput::LONG, ADDRESS_BILLING_SORTING_CODE },
70    { DetailInput::LONG, ADDRESS_BILLING_COUNTRY },
71    { DetailInput::LONG, PHONE_BILLING_WHOLE_NUMBER },
72    { DetailInput::LONG, CREDIT_CARD_NUMBER },
73    { DetailInput::LONG, CREDIT_CARD_EXP_MONTH },
74    { DetailInput::LONG, CREDIT_CARD_EXP_4_DIGIT_YEAR },
75    { DetailInput::LONG, CREDIT_CARD_VERIFICATION_CODE },
76  };
77  common::BuildInputs(kCcBillingInputs, arraysize(kCcBillingInputs), inputs);
78}
79
80// Constructs |inputs| for the SECTION_SHIPPING section.
81void BuildShippingInputs(DetailInputs* inputs) {
82  const DetailInput kShippingInputs[] = {
83    { DetailInput::LONG, NAME_FULL },
84    { DetailInput::LONG, ADDRESS_HOME_STREET_ADDRESS },
85    { DetailInput::LONG, ADDRESS_HOME_CITY },
86    { DetailInput::LONG, ADDRESS_HOME_DEPENDENT_LOCALITY },
87    { DetailInput::LONG, ADDRESS_HOME_STATE },
88    { DetailInput::LONG, ADDRESS_HOME_ZIP },
89    { DetailInput::LONG, ADDRESS_HOME_SORTING_CODE },
90    { DetailInput::LONG, ADDRESS_HOME_COUNTRY },
91    { DetailInput::LONG, PHONE_HOME_WHOLE_NUMBER },
92  };
93  common::BuildInputs(kShippingInputs, arraysize(kShippingInputs), inputs);
94}
95
96base::string16 NullGetInfo(const AutofillType& type) {
97  return base::string16();
98}
99
100void FillOutputForSectionWithComparator(
101    DialogSection section,
102    const DetailInputs& inputs,
103    const FormStructure::InputFieldComparator& compare,
104    FormStructure& form_structure,
105    FullWallet* full_wallet,
106    const base::string16& email_address) {
107  if ((section == SECTION_CC_BILLING && !full_wallet->billing_address()) ||
108      (section == SECTION_SHIPPING && !full_wallet->shipping_address())) {
109    return;
110  }
111
112  base::Callback<base::string16(const AutofillType&)> get_info =
113      base::Bind(&FullWallet::GetInfo,
114                 base::Unretained(full_wallet),
115                 g_browser_process->GetApplicationLocale());
116
117  std::vector<ServerFieldType> types = common::TypesFromInputs(inputs);
118  form_structure.FillFields(types,
119                            compare,
120                            get_info,
121                            g_browser_process->GetApplicationLocale());
122}
123
124void FillOutputForSection(
125    DialogSection section,
126    FormStructure& form_structure,
127    FullWallet* full_wallet,
128    const base::string16& email_address) {
129  DCHECK(section == SECTION_CC_BILLING || section == SECTION_SHIPPING);
130  DetailInputs inputs;
131  if (section == SECTION_CC_BILLING)
132    BuildCcBillingInputs(&inputs);
133  else
134    BuildShippingInputs(&inputs);
135
136  FillOutputForSectionWithComparator(
137      section, inputs,
138      base::Bind(common::ServerTypeMatchesField, section),
139      form_structure, full_wallet, email_address);
140
141  if (section == SECTION_CC_BILLING) {
142    // Email is hidden while using Wallet, special case it.
143    for (size_t i = 0; i < form_structure.field_count(); ++i) {
144      AutofillField* field = form_structure.field(i);
145      if (field->Type().GetStorableType() == EMAIL_ADDRESS)
146        field->value = email_address;
147    }
148  }
149}
150
151// Returns true if |input_type| in |section| is needed for |form_structure|.
152bool IsSectionInputUsedInFormStructure(DialogSection section,
153                                       ServerFieldType input_type,
154                                       const FormStructure& form_structure) {
155  for (size_t i = 0; i < form_structure.field_count(); ++i) {
156    const AutofillField* field = form_structure.field(i);
157    if (field && common::ServerTypeMatchesField(section, input_type, *field))
158      return true;
159  }
160  return false;
161}
162
163// Returns true if one of |inputs| in |section| is needed for |form_structure|.
164bool IsSectionInputsUsedInFormStructure(DialogSection section,
165                                        const ServerFieldType* input_types,
166                                        const size_t input_types_size,
167                                        const FormStructure& form_structure) {
168  for (size_t i = 0; i < input_types_size; ++i) {
169    if (IsSectionInputUsedInFormStructure(
170        section, input_types[i], form_structure)) {
171      return true;
172    }
173  }
174  return false;
175}
176
177}  // namespace
178
179
180// static
181base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create(
182    content::WebContents* contents,
183    const FormData& form_structure,
184    const GURL& source_url,
185    const AutofillManagerDelegate::ResultCallback& callback) {
186  // AutofillDialogControllerAndroid owns itself.
187  AutofillDialogControllerAndroid* autofill_dialog_controller =
188      new AutofillDialogControllerAndroid(contents,
189                                          form_structure,
190                                          source_url,
191                                          callback);
192  return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
193}
194
195#if defined(ENABLE_AUTOFILL_DIALOG)
196// static
197base::WeakPtr<AutofillDialogController>
198AutofillDialogController::Create(
199    content::WebContents* contents,
200    const FormData& form_structure,
201    const GURL& source_url,
202    const AutofillManagerDelegate::ResultCallback& callback) {
203  return AutofillDialogControllerAndroid::Create(contents,
204                                                 form_structure,
205                                                 source_url,
206                                                 callback);
207}
208
209// static
210void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {}
211
212// static
213void AutofillDialogController::RegisterProfilePrefs(
214    user_prefs::PrefRegistrySyncable* registry) {
215  registry->RegisterDictionaryPref(
216      ::prefs::kAutofillDialogDefaults,
217      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
218}
219#endif  // defined(ENABLE_AUTOFILL_DIALOG)
220
221AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
222  if (java_object_.is_null())
223    return;
224
225  JNIEnv* env = base::android::AttachCurrentThread();
226  Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
227}
228
229void AutofillDialogControllerAndroid::Show() {
230  JNIEnv* env = base::android::AttachCurrentThread();
231  dialog_shown_timestamp_ = base::Time::Now();
232
233  // The Autofill dialog is shown in response to a message from the renderer and
234  // as such, it can only be made in the context of the current document. A call
235  // to GetActiveEntry would return a pending entry, if there was one, which
236  // would be a security bug. Therefore, we use the last committed URL for the
237  // access checks.
238  const GURL& current_url = contents_->GetLastCommittedURL();
239  invoked_from_same_origin_ =
240      current_url.GetOrigin() == source_url_.GetOrigin();
241
242  // Fail if the dialog factory (e.g. SDK) doesn't support cross-origin calls.
243  if (!Java_AutofillDialogControllerAndroid_isDialogAllowed(
244          env,
245          invoked_from_same_origin_)) {
246    callback_.Run(
247        AutofillManagerDelegate::AutocompleteResultErrorDisabled,
248        base::ASCIIToUTF16("Cross-origin form invocations are not supported."),
249        NULL);
250    delete this;
251    return;
252  }
253
254  // Determine what field types should be included in the dialog.
255  bool has_types = false;
256  bool has_sections = false;
257  form_structure_.ParseFieldTypesFromAutocompleteAttributes(
258      &has_types, &has_sections);
259
260  // Fail if the author didn't specify autocomplete types, or
261  // if the dialog shouldn't be shown in a given circumstances.
262  if (!has_types) {
263    callback_.Run(
264        AutofillManagerDelegate::AutocompleteResultErrorDisabled,
265        base::ASCIIToUTF16("Form is missing autocomplete attributes."),
266        NULL);
267    delete this;
268    return;
269  }
270
271  // Fail if the author didn't ask for at least some kind of credit card
272  // information.
273  bool has_credit_card_field = false;
274  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
275    AutofillType type = form_structure_.field(i)->Type();
276    if (type.html_type() != HTML_TYPE_UNKNOWN && type.group() == CREDIT_CARD) {
277      has_credit_card_field = true;
278      break;
279    }
280  }
281
282  if (!has_credit_card_field) {
283    callback_.Run(
284        AutofillManagerDelegate::AutocompleteResultErrorDisabled,
285        base::ASCIIToUTF16("Form is not a payment form (must contain "
286                           "some autocomplete=\"cc-*\" fields). "),
287        NULL);
288    delete this;
289    return;
290  }
291
292  // Log any relevant UI metrics and security exceptions.
293  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
294
295  GetMetricLogger().LogDialogSecurityMetric(
296      AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
297
298  if (!invoked_from_same_origin_) {
299    GetMetricLogger().LogDialogSecurityMetric(
300        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
301  }
302
303  const ServerFieldType full_billing_is_necessary_if[] = {
304      ADDRESS_BILLING_LINE1,
305      ADDRESS_BILLING_LINE2,
306      ADDRESS_BILLING_APT_NUM,
307      ADDRESS_BILLING_CITY,
308      ADDRESS_BILLING_STATE,
309      // ADDRESS_BILLING_ZIP,  // Postal code alone is a short form.
310      ADDRESS_BILLING_COUNTRY,
311      ADDRESS_BILLING_STREET_ADDRESS,
312      ADDRESS_BILLING_DEPENDENT_LOCALITY,
313      ADDRESS_BILLING_SORTING_CODE,
314      PHONE_BILLING_WHOLE_NUMBER
315  };
316  const ServerFieldType billing_phone_number_is_necessary_if[] = {
317      PHONE_BILLING_WHOLE_NUMBER
318  };
319  const ServerFieldType shipping_phone_number_is_necessary_if[] = {
320      PHONE_HOME_WHOLE_NUMBER
321  };
322  const bool request_full_billing_address =
323      IsSectionInputsUsedInFormStructure(
324          SECTION_BILLING,
325          full_billing_is_necessary_if,
326          arraysize(full_billing_is_necessary_if),
327          form_structure_);
328  const bool request_phone_numbers =
329      IsSectionInputsUsedInFormStructure(
330          SECTION_BILLING,
331          billing_phone_number_is_necessary_if,
332          arraysize(billing_phone_number_is_necessary_if),
333          form_structure_) ||
334      IsSectionInputsUsedInFormStructure(
335          SECTION_SHIPPING,
336          shipping_phone_number_is_necessary_if,
337          arraysize(shipping_phone_number_is_necessary_if),
338          form_structure_);
339
340  bool request_shipping_address = false;
341  {
342    DetailInputs inputs;
343    BuildShippingInputs(&inputs);
344    request_shipping_address = form_structure_.FillFields(
345        common::TypesFromInputs(inputs),
346        base::Bind(common::ServerTypeMatchesField, SECTION_SHIPPING),
347        base::Bind(NullGetInfo),
348        g_browser_process->GetApplicationLocale());
349  }
350
351  bool last_used_choice_is_autofill = false;
352  base::string16 last_used_account_name;
353  std::string last_used_billing;
354  std::string last_used_shipping;
355  std::string last_used_credit_card;
356  {
357    const base::DictionaryValue* defaults =
358        profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
359    if (defaults) {
360      defaults->GetString(kLastUsedAccountName, &last_used_account_name);
361      defaults->GetBoolean(kLastUsedChoiceIsAutofill,
362                           &last_used_choice_is_autofill);
363      defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
364      defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
365      defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
366    } else {
367      DLOG(ERROR) << "Failed to read AutofillDialog preferences";
368    }
369  }
370
371  const bool incognito_mode = profile_->IsOffTheRecord();
372  if (incognito_mode)
373    last_used_choice_is_autofill = true;
374
375  ScopedJavaLocalRef<jstring> jlast_used_account_name =
376      base::android::ConvertUTF16ToJavaString(
377          env, last_used_account_name);
378  ScopedJavaLocalRef<jstring> jlast_used_billing =
379      base::android::ConvertUTF8ToJavaString(
380          env, last_used_billing);
381  ScopedJavaLocalRef<jstring> jlast_used_shipping =
382      base::android::ConvertUTF8ToJavaString(
383          env, last_used_shipping);
384  ScopedJavaLocalRef<jstring> jlast_used_card =
385      base::android::ConvertUTF8ToJavaString(
386          env, last_used_credit_card);
387  ScopedJavaLocalRef<jstring> jmerchant_domain =
388      base::android::ConvertUTF8ToJavaString(
389          env, source_url_.GetOrigin().spec());
390  const std::set<base::string16> available_shipping_countries =
391      form_structure_.PossibleValues(ADDRESS_HOME_COUNTRY);
392  ScopedJavaLocalRef<jobjectArray> jshipping_countries =
393      base::android::ToJavaArrayOfStrings(
394          env,
395          std::vector<base::string16>(available_shipping_countries.begin(),
396                                      available_shipping_countries.end()));
397  const std::set<base::string16> available_credit_card_types =
398      form_structure_.PossibleValues(CREDIT_CARD_TYPE);
399  ScopedJavaLocalRef<jobjectArray> jcredit_card_types =
400      base::android::ToJavaArrayOfStrings(
401          env,
402          std::vector<base::string16>(available_credit_card_types.begin(),
403                                      available_credit_card_types.end()));
404
405  java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
406      env,
407      reinterpret_cast<intptr_t>(this),
408      WindowAndroidHelper::FromWebContents(contents_)->
409          GetWindowAndroid()->GetJavaObject().obj(),
410      request_full_billing_address, request_shipping_address,
411      request_phone_numbers, incognito_mode,
412      last_used_choice_is_autofill, jlast_used_account_name.obj(),
413      jlast_used_billing.obj(), jlast_used_shipping.obj(),
414      jlast_used_card.obj(),
415      jmerchant_domain.obj(),
416      jshipping_countries.obj(),
417      jcredit_card_types.obj()));
418}
419
420void AutofillDialogControllerAndroid::Hide() {
421  delete this;
422}
423
424void AutofillDialogControllerAndroid::TabActivated() {}
425
426// static
427bool AutofillDialogControllerAndroid::
428    RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
429  return RegisterNativesImpl(env);
430}
431
432void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
433                                                   jobject obj) {
434  LogOnCancelMetrics();
435  callback_.Run(AutofillManagerDelegate::AutocompleteResultErrorCancel,
436                base::string16(),
437                NULL);
438}
439
440void AutofillDialogControllerAndroid::DialogContinue(
441    JNIEnv* env,
442    jobject obj,
443    jobject wallet,
444    jboolean jlast_used_choice_is_autofill,
445    jstring jlast_used_account_name,
446    jstring jlast_used_billing,
447    jstring jlast_used_shipping,
448    jstring jlast_used_card) {
449  const base::string16 email =
450      AutofillDialogResult::GetWalletEmail(env, wallet);
451  const std::string google_transaction_id =
452      AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
453
454  const base::string16 last_used_account_name =
455      base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
456  const std::string last_used_billing =
457      base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
458  const std::string last_used_shipping =
459      base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
460  const std::string last_used_card =
461      base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
462
463  scoped_ptr<FullWallet> full_wallet =
464      AutofillDialogResult::ConvertFromJava(env, wallet);
465  FillOutputForSection(
466      SECTION_CC_BILLING, form_structure_, full_wallet.get(), email);
467  FillOutputForSection(
468      SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
469
470  {
471    DictionaryPrefUpdate updater(profile_->GetPrefs(),
472                                 ::prefs::kAutofillDialogDefaults);
473    base::DictionaryValue* defaults = updater.Get();
474    if (defaults) {
475      const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
476      defaults->SetString(kLastUsedAccountName, last_used_account_name);
477      defaults->SetBoolean(kLastUsedChoiceIsAutofill,
478                           last_used_choice_is_autofill);
479      if (!last_used_billing.empty())
480        defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
481      if (!last_used_shipping.empty())
482        defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
483      if (!last_used_card.empty())
484        defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
485    } else {
486      DLOG(ERROR) << "Failed to save AutofillDialog preferences";
487    }
488  }
489
490  LogOnFinishSubmitMetrics();
491
492  // Callback should be called as late as possible.
493  callback_.Run(AutofillManagerDelegate::AutocompleteResultSuccess,
494                base::string16(),
495                &form_structure_);
496
497  // This might delete us.
498  Hide();
499}
500
501AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
502    content::WebContents* contents,
503    const FormData& form_structure,
504    const GURL& source_url,
505    const AutofillManagerDelegate::ResultCallback& callback)
506    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
507      contents_(contents),
508      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
509      form_structure_(form_structure),
510      invoked_from_same_origin_(true),
511      source_url_(source_url),
512      callback_(callback),
513      cares_about_shipping_(true),
514      weak_ptr_factory_(this),
515      was_ui_latency_logged_(false) {
516  DCHECK(!callback_.is_null());
517}
518
519void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
520  GetMetricLogger().LogDialogUiDuration(
521      base::Time::Now() - dialog_shown_timestamp_,
522      AutofillMetrics::DIALOG_ACCEPTED);
523
524  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
525}
526
527void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
528  GetMetricLogger().LogDialogUiDuration(
529      base::Time::Now() - dialog_shown_timestamp_,
530      AutofillMetrics::DIALOG_CANCELED);
531
532  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
533}
534
535}  // namespace autofill
536