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