autofill_dialog_controller_android.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/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"
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#if defined(ENABLE_AUTOFILL_DIALOG)
158// static
159base::WeakPtr<AutofillDialogController>
160AutofillDialogController::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,
166                                                 form_structure,
167                                                 source_url,
168                                                 callback);
169}
170
171// static
172void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {}
173
174// static
175void AutofillDialogController::RegisterProfilePrefs(
176    user_prefs::PrefRegistrySyncable* registry) {
177  registry->RegisterDictionaryPref(
178      ::prefs::kAutofillDialogDefaults,
179      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
180}
181#endif  // defined(ENABLE_AUTOFILL_DIALOG)
182
183AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
184  if (java_object_.is_null())
185    return;
186
187  JNIEnv* env = base::android::AttachCurrentThread();
188  Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
189}
190
191void AutofillDialogControllerAndroid::Show() {
192  JNIEnv* env = base::android::AttachCurrentThread();
193  dialog_shown_timestamp_ = base::Time::Now();
194
195  const GURL& current_url = contents_->GetLastCommittedURL();
196  invoked_from_same_origin_ =
197      current_url.GetOrigin() == source_url_.GetOrigin();
198
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);
204
205  // Fail if the author didn't specify autocomplete types, or
206  // if the dialog shouldn't be shown in a given circumstances.
207  if (!has_types ||
208      !Java_AutofillDialogControllerAndroid_isDialogAllowed(
209          env,
210          RequestingCreditCardInfo(),
211          TransmissionWillBeSecure(),
212          invoked_from_same_origin_)) {
213    callback_.Run(NULL);
214    delete this;
215    return;
216  }
217
218  // Log any relevant UI metrics and security exceptions.
219  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
220
221  GetMetricLogger().LogDialogSecurityMetric(
222      AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
223
224  if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
225    GetMetricLogger().LogDialogSecurityMetric(
226        AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
227  }
228
229  if (!invoked_from_same_origin_) {
230    GetMetricLogger().LogDialogSecurityMetric(
231        AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
232  }
233
234  const ServerFieldType full_billing_is_necessary_if[] = {
235      ADDRESS_BILLING_LINE1,
236      ADDRESS_BILLING_LINE2,
237      ADDRESS_BILLING_CITY,
238      ADDRESS_BILLING_STATE,
239      PHONE_BILLING_WHOLE_NUMBER
240  };
241  const bool request_full_billing_address =
242      IsSectionInputsUsedInFormStructure(
243          SECTION_BILLING,
244          full_billing_is_necessary_if,
245          arraysize(full_billing_is_necessary_if),
246          form_structure_);
247  const bool request_phone_numbers =
248      IsSectionInputUsedInFormStructure(
249          SECTION_BILLING,
250          PHONE_BILLING_WHOLE_NUMBER,
251          form_structure_) ||
252      IsSectionInputUsedInFormStructure(
253          SECTION_SHIPPING,
254          PHONE_HOME_WHOLE_NUMBER,
255          form_structure_);
256
257  bool request_shipping_address = false;
258  {
259    DetailInputs inputs;
260    common::BuildInputsForSection(SECTION_SHIPPING, &inputs);
261    EmptyDataModelWrapper empty_wrapper;
262    request_shipping_address = empty_wrapper.FillFormStructure(
263        inputs,
264        base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING),
265        &form_structure_);
266  }
267
268  const bool incognito_mode = profile_->IsOffTheRecord();
269
270  bool last_used_choice_is_autofill = false;
271  base::string16 last_used_account_name;
272  std::string last_used_billing;
273  std::string last_used_shipping;
274  std::string last_used_credit_card;
275  {
276    const base::DictionaryValue* defaults =
277        profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
278    if (defaults) {
279      defaults->GetString(kLastUsedAccountName, &last_used_account_name);
280      defaults->GetBoolean(kLastUsedChoiceIsAutofill,
281                           &last_used_choice_is_autofill);
282      defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
283      defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
284      defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
285    } else {
286      DLOG(ERROR) << "Failed to read AutofillDialog preferences";
287    }
288  }
289
290  if (contents_->GetBrowserContext()->IsOffTheRecord())
291    last_used_choice_is_autofill = true;
292
293  ScopedJavaLocalRef<jstring> jlast_used_account_name =
294      base::android::ConvertUTF16ToJavaString(
295          env, last_used_account_name);
296  ScopedJavaLocalRef<jstring> jlast_used_billing =
297      base::android::ConvertUTF8ToJavaString(
298          env, last_used_billing);
299  ScopedJavaLocalRef<jstring> jlast_used_shipping =
300      base::android::ConvertUTF8ToJavaString(
301          env, last_used_shipping);
302  ScopedJavaLocalRef<jstring> jlast_used_card =
303      base::android::ConvertUTF8ToJavaString(
304          env, last_used_credit_card);
305  ScopedJavaLocalRef<jstring> jmerchant_domain =
306      base::android::ConvertUTF8ToJavaString(
307          env, source_url_.GetOrigin().spec());
308  java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
309      env,
310      reinterpret_cast<intptr_t>(this),
311      WindowAndroidHelper::FromWebContents(contents_)->
312          GetWindowAndroid()->GetJavaObject().obj(),
313      request_full_billing_address, request_shipping_address,
314      request_phone_numbers, incognito_mode,
315      last_used_choice_is_autofill, jlast_used_account_name.obj(),
316      jlast_used_billing.obj(), jlast_used_shipping.obj(),
317      jlast_used_card.obj(),
318      jmerchant_domain.obj()));
319}
320
321void AutofillDialogControllerAndroid::Hide() {
322  // TODO(aruslan): http://crbug.com/177373 Autocheckout.
323  NOTIMPLEMENTED();
324}
325
326void AutofillDialogControllerAndroid::TabActivated() {}
327
328// static
329bool AutofillDialogControllerAndroid::
330    RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
331  return RegisterNativesImpl(env);
332}
333
334void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
335                                                   jobject obj) {
336  LogOnCancelMetrics();
337  callback_.Run(NULL);
338}
339
340void AutofillDialogControllerAndroid::DialogContinue(
341    JNIEnv* env,
342    jobject obj,
343    jobject wallet,
344    jboolean jlast_used_choice_is_autofill,
345    jstring jlast_used_account_name,
346    jstring jlast_used_billing,
347    jstring jlast_used_shipping,
348    jstring jlast_used_card) {
349  const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet);
350  const std::string google_transaction_id =
351      AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
352
353  const string16 last_used_account_name =
354      base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
355  const std::string last_used_billing =
356      base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
357  const std::string last_used_shipping =
358      base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
359  const std::string last_used_card =
360      base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
361
362  scoped_ptr<wallet::FullWallet> full_wallet =
363      AutofillDialogResult::ConvertFromJava(env, wallet);
364  FillOutputForSection(
365      SECTION_CC_BILLING, form_structure_, full_wallet.get(), email);
366  FillOutputForSection(
367      SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
368
369  {
370    DictionaryPrefUpdate updater(profile_->GetPrefs(),
371                                 ::prefs::kAutofillDialogDefaults);
372    base::DictionaryValue* defaults = updater.Get();
373    if (defaults) {
374      const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
375      defaults->SetString(kLastUsedAccountName, last_used_account_name);
376      defaults->SetBoolean(kLastUsedChoiceIsAutofill,
377                           last_used_choice_is_autofill);
378      if (!last_used_billing.empty())
379        defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
380      if (!last_used_shipping.empty())
381        defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
382      if (!last_used_card.empty())
383        defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
384    } else {
385      LOG(ERROR) << "Failed to save AutofillDialog preferences";
386    }
387  }
388
389  LogOnFinishSubmitMetrics();
390
391  // Callback should be called as late as possible.
392  callback_.Run(&form_structure_);
393
394  // This might delete us.
395  Hide();
396}
397
398AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
399    content::WebContents* contents,
400    const FormData& form_structure,
401    const GURL& source_url,
402    const base::Callback<void(const FormStructure*)>& callback)
403    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
404      contents_(contents),
405      initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
406      form_structure_(form_structure),
407      invoked_from_same_origin_(true),
408      source_url_(source_url),
409      callback_(callback),
410      cares_about_shipping_(true),
411      weak_ptr_factory_(this),
412      was_ui_latency_logged_(false) {
413  DCHECK(!callback_.is_null());
414}
415
416bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const {
417  DCHECK_GT(form_structure_.field_count(), 0U);
418
419  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
420    AutofillType type = form_structure_.field(i)->Type();
421    if (common::IsCreditCardType(type.GetStorableType()))
422      return true;
423  }
424
425  return false;
426}
427
428bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const {
429  return source_url_.SchemeIs(content::kHttpsScheme);
430}
431
432void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
433  GetMetricLogger().LogDialogUiDuration(
434      base::Time::Now() - dialog_shown_timestamp_,
435      AutofillMetrics::DIALOG_ACCEPTED);
436
437  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
438}
439
440void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
441  GetMetricLogger().LogDialogUiDuration(
442      base::Time::Now() - dialog_shown_timestamp_,
443      AutofillMetrics::DIALOG_CANCELED);
444
445  GetMetricLogger().LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
446}
447
448}  // namespace autofill
449