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