autofill_dialog_controller_android.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/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/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"
46#include "url/gurl.h"
47
48namespace autofill {
49
50namespace {
51
52// Keys in kAutofillDialogDefaults pref dictionary (do not change these values).
53const char kLastUsedAccountName[] = "last_used_account_name";
54const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill";
55const char kLastUsedBillingAddressGuid[] = "last_used_billing";
56const char kLastUsedShippingAddressGuid[] = "last_used_shipping";
57const char kLastUsedCreditCardGuid[] = "last_used_card";
58
59scoped_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>();
64
65    return scoped_ptr<DataModelWrapper>(
66        new FullWalletBillingWrapper(full_wallet));
67  }
68  if (section == SECTION_SHIPPING) {
69    if (!full_wallet->shipping_address())
70      return scoped_ptr<DataModelWrapper>();
71
72    return scoped_ptr<DataModelWrapper>(
73        new FullWalletShippingWrapper(full_wallet));
74  }
75  NOTREACHED();
76  return scoped_ptr<DataModelWrapper>();
77}
78
79void 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);
85  if (wrapper)
86    wrapper->FillFormStructure(inputs, compare, &form_structure);
87}
88
89void FillOutputForSection(
90    DialogSection section,
91    FormStructure& form_structure,
92    wallet::FullWallet* full_wallet,
93    const base::string16& email_address) {
94  DetailInputs inputs;
95  common::BuildInputsForSection(section, &inputs);
96
97  FillOutputForSectionWithComparator(
98      section, inputs,
99      base::Bind(common::DetailInputMatchesField, section),
100      form_structure, full_wallet, email_address);
101
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;
108    }
109  }
110}
111
112// Returns true if |input_type| in |section| is needed for |form_structure|.
113bool IsSectionInputUsedInFormStructure(DialogSection section,
114                                       ServerFieldType input_type,
115                                       const FormStructure& form_structure) {
116  const DetailInput input = { 0, 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))
120      return true;
121  }
122  return false;
123}
124
125// Returns true if one of |inputs| in |section| is needed for |form_structure|.
126bool 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)) {
133      return true;
134    }
135  }
136  return false;
137}
138
139}  // namespace
140
141
142// static
143base::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,
151                                          form_structure,
152                                          source_url,
153                                          callback);
154  return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
155}
156
157// static
158void AutofillDialogControllerAndroid::RegisterProfilePrefs(
159    user_prefs::PrefRegistrySyncable* registry) {
160  registry->RegisterDictionaryPref(
161      ::prefs::kAutofillDialogDefaults,
162      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
163}
164
165#if defined(ENABLE_AUTOFILL_DIALOG)
166// static
167base::WeakPtr<AutofillDialogController>
168AutofillDialogController::Create(
169    content::WebContents* contents,
170    const FormData& form_structure,
171    const GURL& source_url,
172    const base::Callback<void(const FormStructure*)>& callback) {
173  return AutofillDialogControllerAndroid::Create(contents,
174                                                 form_structure,
175                                                 source_url,
176                                                 callback);
177}
178
179// static
180void AutofillDialogController::RegisterProfilePrefs(
181    user_prefs::PrefRegistrySyncable* registry) {
182  AutofillDialogControllerAndroid::RegisterProfilePrefs(registry);
183}
184#endif  // defined(ENABLE_AUTOFILL_DIALOG)
185
186AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
187  if (java_object_.is_null())
188    return;
189
190  JNIEnv* env = base::android::AttachCurrentThread();
191  Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
192}
193
194void AutofillDialogControllerAndroid::Show() {
195  JNIEnv* env = base::android::AttachCurrentThread();
196  dialog_shown_timestamp_ = base::Time::Now();
197
198  content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
199  const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
200  invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
201
202  // Determine what field types should be included in the dialog.
203  bool has_types = false;
204  bool has_sections = false;
205  form_structure_.ParseFieldTypesFromAutocompleteAttributes(
206      &has_types, &has_sections);
207
208  // Fail if the author didn't specify autocomplete types, or
209  // if the dialog shouldn't be shown in a given circumstances.
210  if (!has_types ||
211      !Java_AutofillDialogControllerAndroid_isDialogAllowed(
212          env,
213          RequestingCreditCardInfo(),
214          TransmissionWillBeSecure(),
215          invoked_from_same_origin_)) {
216    callback_.Run(NULL);
217    delete this;
218    return;
219  }
220
221  // Log any relevant UI metrics and security exceptions.
222  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
223
224  GetMetricLogger().LogDialogSecurityMetric(
225      AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
226
227  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
228    GetMetricLogger().LogDialogSecurityMetric(
229        AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
230  }
231
232  if (!invoked_from_same_origin_) {
233    GetMetricLogger().LogDialogSecurityMetric(
234        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
235  }
236
237  const ServerFieldType full_billing_is_necessary_if[] = {
238      ADDRESS_BILLING_LINE1,
239      ADDRESS_BILLING_LINE2,
240      ADDRESS_BILLING_CITY,
241      ADDRESS_BILLING_STATE,
242      PHONE_BILLING_WHOLE_NUMBER
243  };
244  const bool request_full_billing_address =
245      IsSectionInputsUsedInFormStructure(
246          SECTION_BILLING,
247          full_billing_is_necessary_if,
248          arraysize(full_billing_is_necessary_if),
249          form_structure_);
250  const bool request_phone_numbers =
251      IsSectionInputUsedInFormStructure(
252          SECTION_BILLING,
253          PHONE_BILLING_WHOLE_NUMBER,
254          form_structure_) ||
255      IsSectionInputUsedInFormStructure(
256          SECTION_SHIPPING,
257          PHONE_HOME_WHOLE_NUMBER,
258          form_structure_);
259
260  bool request_shipping_address = false;
261  {
262    DetailInputs inputs;
263    common::BuildInputsForSection(SECTION_SHIPPING, &inputs);
264    EmptyDataModelWrapper empty_wrapper;
265    request_shipping_address = empty_wrapper.FillFormStructure(
266        inputs,
267        base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING),
268        &form_structure_);
269  }
270
271  const bool incognito_mode = profile_->IsOffTheRecord();
272
273  bool last_used_choice_is_autofill = false;
274  base::string16 last_used_account_name;
275  std::string last_used_billing;
276  std::string last_used_shipping;
277  std::string last_used_credit_card;
278  {
279    const base::DictionaryValue* defaults =
280        profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
281    if (defaults) {
282      defaults->GetString(kLastUsedAccountName, &last_used_account_name);
283      defaults->GetBoolean(kLastUsedChoiceIsAutofill,
284                           &last_used_choice_is_autofill);
285      defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
286      defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
287      defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
288    } else {
289      DLOG(ERROR) << "Failed to read AutofillDialog preferences";
290    }
291  }
292
293  if (contents_->GetBrowserContext()->IsOffTheRecord())
294    last_used_choice_is_autofill = true;
295
296  ScopedJavaLocalRef<jstring> jlast_used_account_name =
297      base::android::ConvertUTF16ToJavaString(
298          env, last_used_account_name);
299  ScopedJavaLocalRef<jstring> jlast_used_billing =
300      base::android::ConvertUTF8ToJavaString(
301          env, last_used_billing);
302  ScopedJavaLocalRef<jstring> jlast_used_shipping =
303      base::android::ConvertUTF8ToJavaString(
304          env, last_used_shipping);
305  ScopedJavaLocalRef<jstring> jlast_used_card =
306      base::android::ConvertUTF8ToJavaString(
307          env, last_used_credit_card);
308  ScopedJavaLocalRef<jstring> jmerchant_domain =
309      base::android::ConvertUTF8ToJavaString(
310          env, source_url_.GetOrigin().spec());
311  java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
312      env,
313      reinterpret_cast<jint>(this),
314      WindowAndroidHelper::FromWebContents(contents_)->
315          GetWindowAndroid()->GetJavaObject().obj(),
316      request_full_billing_address, request_shipping_address,
317      request_phone_numbers, incognito_mode,
318      last_used_choice_is_autofill, jlast_used_account_name.obj(),
319      jlast_used_billing.obj(), jlast_used_shipping.obj(),
320      jlast_used_card.obj(),
321      jmerchant_domain.obj()));
322}
323
324void AutofillDialogControllerAndroid::Hide() {
325  // TODO(aruslan): http://crbug.com/177373 Autocheckout.
326  NOTIMPLEMENTED();
327}
328
329void AutofillDialogControllerAndroid::TabActivated() {}
330
331// static
332bool AutofillDialogControllerAndroid::
333    RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
334  return RegisterNativesImpl(env);
335}
336
337void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
338                                                   jobject obj) {
339  LogOnCancelMetrics();
340  callback_.Run(NULL);
341}
342
343void AutofillDialogControllerAndroid::DialogContinue(
344    JNIEnv* env,
345    jobject obj,
346    jobject wallet,
347    jboolean jlast_used_choice_is_autofill,
348    jstring jlast_used_account_name,
349    jstring jlast_used_billing,
350    jstring jlast_used_shipping,
351    jstring jlast_used_card) {
352  const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet);
353  const std::string google_transaction_id =
354      AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
355
356  const string16 last_used_account_name =
357      base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
358  const std::string last_used_billing =
359      base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
360  const std::string last_used_shipping =
361      base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
362  const std::string last_used_card =
363      base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
364
365  scoped_ptr<wallet::FullWallet> full_wallet =
366      AutofillDialogResult::ConvertFromJava(env, wallet);
367  FillOutputForSection(
368      SECTION_CC_BILLING, form_structure_, full_wallet.get(), email);
369  FillOutputForSection(
370      SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
371
372  {
373    DictionaryPrefUpdate updater(profile_->GetPrefs(),
374                                 ::prefs::kAutofillDialogDefaults);
375    base::DictionaryValue* defaults = updater.Get();
376    if (defaults) {
377      const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
378      defaults->SetString(kLastUsedAccountName, last_used_account_name);
379      defaults->SetBoolean(kLastUsedChoiceIsAutofill,
380                           last_used_choice_is_autofill);
381      if (!last_used_billing.empty())
382        defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
383      if (!last_used_shipping.empty())
384        defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
385      if (!last_used_card.empty())
386        defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
387    } else {
388      LOG(ERROR) << "Failed to save AutofillDialog preferences";
389    }
390  }
391
392  LogOnFinishSubmitMetrics();
393
394  // Callback should be called as late as possible.
395  callback_.Run(&form_structure_);
396
397  // This might delete us.
398  Hide();
399}
400
401AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
402    content::WebContents* contents,
403    const FormData& form_structure,
404    const GURL& source_url,
405    const base::Callback<void(const FormStructure*)>& callback)
406    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
407      contents_(contents),
408      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
409      form_structure_(form_structure),
410      invoked_from_same_origin_(true),
411      source_url_(source_url),
412      callback_(callback),
413      cares_about_shipping_(true),
414      weak_ptr_factory_(this),
415      was_ui_latency_logged_(false) {
416  DCHECK(!callback_.is_null());
417}
418
419bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const {
420  DCHECK_GT(form_structure_.field_count(), 0U);
421
422  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
423    AutofillType type = form_structure_.field(i)->Type();
424    if (common::IsCreditCardType(type.GetStorableType()))
425      return true;
426  }
427
428  return false;
429}
430
431bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const {
432  return source_url_.SchemeIs(content::kHttpsScheme);
433}
434
435void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
436  GetMetricLogger().LogDialogUiDuration(
437      base::Time::Now() - dialog_shown_timestamp_,
438      AutofillMetrics::DIALOG_ACCEPTED);
439
440  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
441}
442
443void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
444  GetMetricLogger().LogDialogUiDuration(
445      base::Time::Now() - dialog_shown_timestamp_,
446      AutofillMetrics::DIALOG_CANCELED);
447
448  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
449}
450
451}  // namespace autofill
452