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