| Index: chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
|
| diff --git a/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc b/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..655edd956d070b93242ddc495586b460e3496e8e
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
|
| @@ -0,0 +1,488 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/ui/android/autofill/autofill_dialog_controller_android.h"
|
| +
|
| +#include "base/android/jni_android.h"
|
| +#include "base/android/jni_array.h"
|
| +#include "base/android/jni_string.h"
|
| +#include "base/android/scoped_java_ref.h"
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "base/prefs/pref_service.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "chrome/browser/autofill/personal_data_manager_factory.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/prefs/scoped_user_pref_update.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| +#include "chrome/browser/ui/android/autofill/autofill_dialog_result.h"
|
| +#include "chrome/browser/ui/android/window_android_helper.h"
|
| +#include "chrome/browser/ui/autofill/autofill_dialog_common.h"
|
| +#include "chrome/browser/ui/autofill/data_model_wrapper.h"
|
| +#include "chrome/common/pref_names.h"
|
| +#include "chrome/common/url_constants.h"
|
| +#include "components/autofill/content/browser/wallet/full_wallet.h"
|
| +#include "components/autofill/core/browser/autofill_metrics.h"
|
| +#include "components/autofill/core/browser/autofill_profile.h"
|
| +#include "components/autofill/core/browser/autofill_type.h"
|
| +#include "components/autofill/core/browser/credit_card.h"
|
| +#include "components/autofill/core/browser/personal_data_manager.h"
|
| +#include "components/autofill/core/common/form_data.h"
|
| +#include "components/user_prefs/pref_registry_syncable.h"
|
| +#include "content/public/browser/navigation_controller.h"
|
| +#include "content/public/browser/navigation_details.h"
|
| +#include "content/public/browser/navigation_entry.h"
|
| +#include "content/public/browser/web_contents.h"
|
| +#include "grit/generated_resources.h"
|
| +#include "jni/AutofillDialogControllerAndroid_jni.h"
|
| +#include "ui/android/window_android.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/models/combobox_model.h"
|
| +#include "ui/base/models/menu_model.h"
|
| +#include "ui/gfx/android/java_bitmap.h"
|
| +#include "ui/gfx/rect.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace autofill {
|
| +
|
| +namespace {
|
| +
|
| +// Keys in kAutofillDialogDefaults pref dictionary (do not change these values).
|
| +const char kLastUsedAccountName[] = "last_used_account_name";
|
| +const char kLastUsedChoiceIsAutofill[] = "last_used_choice_is_autofill";
|
| +const char kLastUsedBillingAddressGuid[] = "last_used_billing";
|
| +const char kLastUsedShippingAddressGuid[] = "last_used_shipping";
|
| +const char kLastUsedCreditCardGuid[] = "last_used_card";
|
| +
|
| +scoped_ptr<DataModelWrapper> CreateWrapper(
|
| + DialogSection section, wallet::FullWallet* full_wallet) {
|
| + if (section == SECTION_CC_BILLING) {
|
| + if (!full_wallet->billing_address())
|
| + return scoped_ptr<DataModelWrapper>();
|
| +
|
| + return scoped_ptr<DataModelWrapper>(
|
| + new FullWalletBillingWrapper(full_wallet));
|
| + }
|
| + if (section == SECTION_SHIPPING) {
|
| + if (!full_wallet->shipping_address())
|
| + return scoped_ptr<DataModelWrapper>();
|
| +
|
| + return scoped_ptr<DataModelWrapper>(
|
| + new FullWalletShippingWrapper(full_wallet));
|
| + }
|
| + NOTREACHED();
|
| + return scoped_ptr<DataModelWrapper>();
|
| +}
|
| +
|
| +void FillOutputForSectionWithComparator(
|
| + DialogSection section, const DetailInputs& inputs,
|
| + const InputFieldComparator& compare,
|
| + FormStructure& form_structure, wallet::FullWallet* full_wallet,
|
| + const base::string16& email_address) {
|
| +
|
| + // Email is hidden while using Wallet, special case it.
|
| + if (section == SECTION_EMAIL) {
|
| + AutofillProfile profile;
|
| + profile.SetRawInfo(EMAIL_ADDRESS, email_address);
|
| + AutofillProfileWrapper profile_wrapper(&profile, 0);
|
| + profile_wrapper.FillFormStructure(inputs, compare, &form_structure);
|
| + return;
|
| + }
|
| +
|
| + scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section, full_wallet);
|
| + if (wrapper)
|
| + wrapper->FillFormStructure(inputs, compare, &form_structure);
|
| +}
|
| +
|
| +void FillOutputForSection(
|
| + DialogSection section,
|
| + FormStructure& form_structure,
|
| + wallet::FullWallet* full_wallet,
|
| + const base::string16& email_address) {
|
| + DetailInputs inputs;
|
| + common::BuildInputsForSection(section, &inputs);
|
| +
|
| + FillOutputForSectionWithComparator(
|
| + section, inputs,
|
| + base::Bind(common::DetailInputMatchesField, section),
|
| + form_structure, full_wallet, email_address);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +// static
|
| +base::WeakPtr<AutofillDialogController> AutofillDialogControllerAndroid::Create(
|
| + content::WebContents* contents,
|
| + const FormData& form_structure,
|
| + const GURL& source_url,
|
| + const DialogType dialog_type,
|
| + const base::Callback<void(const FormStructure*,
|
| + const std::string&)>& callback) {
|
| + // AutofillDialogControllerAndroid owns itself.
|
| + AutofillDialogControllerAndroid* autofill_dialog_controller =
|
| + new AutofillDialogControllerAndroid(contents,
|
| + form_structure,
|
| + source_url,
|
| + dialog_type,
|
| + callback);
|
| + return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
|
| +}
|
| +
|
| +// static
|
| +void AutofillDialogControllerAndroid::RegisterProfilePrefs(
|
| + user_prefs::PrefRegistrySyncable* registry) {
|
| + registry->RegisterDictionaryPref(
|
| + ::prefs::kAutofillDialogDefaults,
|
| + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
|
| +}
|
| +
|
| +// static
|
| +base::WeakPtr<AutofillDialogController>
|
| +AutofillDialogController::Create(
|
| + content::WebContents* contents,
|
| + const FormData& form_structure,
|
| + const GURL& source_url,
|
| + const DialogType dialog_type,
|
| + const base::Callback<void(const FormStructure*,
|
| + const std::string&)>& callback) {
|
| + return AutofillDialogControllerAndroid::Create(contents,
|
| + form_structure,
|
| + source_url,
|
| + dialog_type,
|
| + callback);
|
| +}
|
| +
|
| +// static
|
| +void AutofillDialogController::RegisterProfilePrefs(
|
| + user_prefs::PrefRegistrySyncable* registry) {
|
| + AutofillDialogControllerAndroid::RegisterProfilePrefs(registry);
|
| +}
|
| +
|
| +AutofillDialogControllerAndroid::~AutofillDialogControllerAndroid() {
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + Java_AutofillDialogControllerAndroid_onDestroy(env, java_object_.obj());
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::Show() {
|
| + dialog_shown_timestamp_ = base::Time::Now();
|
| +
|
| + content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
|
| + const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
|
| + invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
|
| +
|
| + // Log any relevant UI metrics and security exceptions.
|
| + GetMetricLogger().LogDialogUiEvent(
|
| + GetDialogType(), AutofillMetrics::DIALOG_UI_SHOWN);
|
| +
|
| + GetMetricLogger().LogDialogSecurityMetric(
|
| + GetDialogType(), AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
|
| +
|
| + if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
|
| + GetMetricLogger().LogDialogSecurityMetric(
|
| + GetDialogType(),
|
| + AutofillMetrics::SECURITY_METRIC_CREDIT_CARD_OVER_HTTP);
|
| + }
|
| +
|
| + if (!invoked_from_same_origin_) {
|
| + GetMetricLogger().LogDialogSecurityMetric(
|
| + GetDialogType(),
|
| + AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
|
| + }
|
| +
|
| + // Determine what field types should be included in the dialog.
|
| + bool has_types = false;
|
| + bool has_sections = false;
|
| + form_structure_.ParseFieldTypesFromAutocompleteAttributes(
|
| + &has_types, &has_sections);
|
| +
|
| + // Fail if the author didn't specify autocomplete types.
|
| + if (!has_types) {
|
| + callback_.Run(NULL, std::string());
|
| + delete this;
|
| + return;
|
| + }
|
| +
|
| + bool request_full_billing_address = true;
|
| + bool request_shipping_address = false;
|
| + bool request_phone_numbers = false;
|
| +
|
| + for (size_t i = 0; i < form_structure_.field_count(); ++i) {
|
| + const ServerFieldType type =
|
| + form_structure_.field(i)->Type().GetStorableType();
|
| + if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER) {
|
| + request_phone_numbers = true;
|
| + }
|
| + if (type == NAME_FULL ||
|
| + type == ADDRESS_HOME_LINE1 || type == ADDRESS_HOME_LINE2 ||
|
| + type == ADDRESS_HOME_CITY || type == ADDRESS_HOME_STATE ||
|
| + type == ADDRESS_HOME_ZIP || type == ADDRESS_HOME_COUNTRY ||
|
| + type == PHONE_HOME_WHOLE_NUMBER) {
|
| + request_shipping_address = true;
|
| + }
|
| + if (type == ADDRESS_BILLING_LINE1 || type == ADDRESS_BILLING_LINE2 ||
|
| + type == ADDRESS_BILLING_CITY || type == ADDRESS_BILLING_STATE ||
|
| + type == PHONE_BILLING_WHOLE_NUMBER) {
|
| + request_full_billing_address = true;
|
| + }
|
| + }
|
| +
|
| + if (request_shipping_address)
|
| + request_full_billing_address = true;
|
| +
|
| + const bool incognito_mode = profile_->IsOffTheRecord();
|
| +
|
| + bool last_used_choice_is_autofill = false;
|
| + base::string16 last_used_account_name;
|
| + std::string last_used_billing;
|
| + std::string last_used_shipping;
|
| + std::string last_used_credit_card;
|
| + {
|
| + const base::DictionaryValue* defaults =
|
| + profile_->GetPrefs()->GetDictionary(::prefs::kAutofillDialogDefaults);
|
| + if (defaults) {
|
| + defaults->GetString(kLastUsedAccountName, &last_used_account_name);
|
| + defaults->GetBoolean(kLastUsedChoiceIsAutofill,
|
| + &last_used_choice_is_autofill);
|
| + defaults->GetString(kLastUsedBillingAddressGuid, &last_used_billing);
|
| + defaults->GetString(kLastUsedShippingAddressGuid, &last_used_shipping);
|
| + defaults->GetString(kLastUsedCreditCardGuid, &last_used_credit_card);
|
| + } else {
|
| + DLOG(ERROR) << "Failed to read AutofillDialog preferences";
|
| + }
|
| + }
|
| +
|
| + if (contents_->GetBrowserContext()->IsOffTheRecord())
|
| + last_used_choice_is_autofill = true;
|
| +
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + ScopedJavaLocalRef<jstring> jlast_used_account_name =
|
| + base::android::ConvertUTF16ToJavaString(
|
| + env, last_used_account_name);
|
| + ScopedJavaLocalRef<jstring> jlast_used_billing =
|
| + base::android::ConvertUTF8ToJavaString(
|
| + env, last_used_billing);
|
| + ScopedJavaLocalRef<jstring> jlast_used_shipping =
|
| + base::android::ConvertUTF8ToJavaString(
|
| + env, last_used_shipping);
|
| + ScopedJavaLocalRef<jstring> jlast_used_card =
|
| + base::android::ConvertUTF8ToJavaString(
|
| + env, last_used_credit_card);
|
| + ScopedJavaLocalRef<jstring> jmerchant_domain =
|
| + base::android::ConvertUTF8ToJavaString(
|
| + env, source_url_.GetOrigin().spec());
|
| + java_object_.Reset(Java_AutofillDialogControllerAndroid_create(
|
| + env,
|
| + reinterpret_cast<jint>(this),
|
| + WindowAndroidHelper::FromWebContents(contents_)->
|
| + GetWindowAndroid()->GetJavaObject().obj(),
|
| + request_full_billing_address, request_shipping_address,
|
| + request_phone_numbers, incognito_mode,
|
| + last_used_choice_is_autofill, jlast_used_account_name.obj(),
|
| + jlast_used_billing.obj(), jlast_used_shipping.obj(),
|
| + jlast_used_card.obj(),
|
| + jmerchant_domain.obj()));
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::Hide() {
|
| + // TODO(aruslan): http://crbug.com/177373 Autocheckout.
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::TabActivated() {}
|
| +
|
| +void AutofillDialogControllerAndroid::AddAutocheckoutStep(
|
| + AutocheckoutStepType step_type) {
|
| + // TODO(aruslan): http://crbug.com/177373 Autocheckout.
|
| + NOTIMPLEMENTED() << " step_type = " << step_type;
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::UpdateAutocheckoutStep(
|
| + AutocheckoutStepType step_type,
|
| + AutocheckoutStepStatus step_status) {
|
| + // TODO(aruslan): http://crbug.com/177373 Autocheckout.
|
| + NOTIMPLEMENTED() << " step_type=" << step_type
|
| + << " step_status=" << step_status;
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::OnAutocheckoutError() {
|
| + // TODO(aruslan): http://crbug.com/177373 Autocheckout.
|
| + NOTIMPLEMENTED();
|
| + DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
|
| + GetMetricLogger().LogAutocheckoutDuration(
|
| + base::Time::Now() - autocheckout_started_timestamp_,
|
| + AutofillMetrics::AUTOCHECKOUT_FAILED);
|
| + SetAutocheckoutState(AUTOCHECKOUT_ERROR);
|
| + autocheckout_started_timestamp_ = base::Time();
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::OnAutocheckoutSuccess() {
|
| + // TODO(aruslan): http://crbug.com/177373 Autocheckout.
|
| + NOTIMPLEMENTED();
|
| + DCHECK_EQ(AUTOCHECKOUT_IN_PROGRESS, autocheckout_state_);
|
| + GetMetricLogger().LogAutocheckoutDuration(
|
| + base::Time::Now() - autocheckout_started_timestamp_,
|
| + AutofillMetrics::AUTOCHECKOUT_SUCCEEDED);
|
| + SetAutocheckoutState(AUTOCHECKOUT_SUCCESS);
|
| + autocheckout_started_timestamp_ = base::Time();
|
| +}
|
| +
|
| +DialogType AutofillDialogControllerAndroid::GetDialogType() const {
|
| + return dialog_type_;
|
| +}
|
| +
|
| +// static
|
| +bool AutofillDialogControllerAndroid::
|
| + RegisterAutofillDialogControllerAndroid(JNIEnv* env) {
|
| + return RegisterNativesImpl(env);
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::DialogCancel(JNIEnv* env,
|
| + jobject obj) {
|
| + if (autocheckout_state_ == AUTOCHECKOUT_NOT_STARTED)
|
| + LogOnCancelMetrics();
|
| +
|
| + if (autocheckout_state_ == AUTOCHECKOUT_IN_PROGRESS) {
|
| + GetMetricLogger().LogAutocheckoutDuration(
|
| + base::Time::Now() - autocheckout_started_timestamp_,
|
| + AutofillMetrics::AUTOCHECKOUT_CANCELLED);
|
| + }
|
| +
|
| + callback_.Run(NULL, std::string());
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::DialogContinue(
|
| + JNIEnv* env,
|
| + jobject obj,
|
| + jobject wallet,
|
| + jboolean jlast_used_choice_is_autofill,
|
| + jstring jlast_used_account_name,
|
| + jstring jlast_used_billing,
|
| + jstring jlast_used_shipping,
|
| + jstring jlast_used_card) {
|
| + const string16 email = AutofillDialogResult::GetWalletEmail(env, wallet);
|
| + const std::string google_transaction_id =
|
| + AutofillDialogResult::GetWalletGoogleTransactionId(env, wallet);
|
| +
|
| + const string16 last_used_account_name =
|
| + base::android::ConvertJavaStringToUTF16(env, jlast_used_account_name);
|
| + const std::string last_used_billing =
|
| + base::android::ConvertJavaStringToUTF8(env, jlast_used_billing);
|
| + const std::string last_used_shipping =
|
| + base::android::ConvertJavaStringToUTF8(env, jlast_used_shipping);
|
| + const std::string last_used_card =
|
| + base::android::ConvertJavaStringToUTF8(env, jlast_used_card);
|
| +
|
| + scoped_ptr<wallet::FullWallet> full_wallet =
|
| + AutofillDialogResult::ConvertFromJava(env, wallet);
|
| + FillOutputForSection(
|
| + SECTION_EMAIL, form_structure_, full_wallet.get(), email);
|
| + FillOutputForSection(
|
| + SECTION_CC_BILLING, form_structure_, full_wallet.get(), email);
|
| + FillOutputForSection(
|
| + SECTION_SHIPPING, form_structure_, full_wallet.get(), email);
|
| +
|
| + {
|
| + DictionaryPrefUpdate updater(profile_->GetPrefs(),
|
| + ::prefs::kAutofillDialogDefaults);
|
| + base::DictionaryValue* defaults = updater.Get();
|
| + if (defaults) {
|
| + const bool last_used_choice_is_autofill = !!jlast_used_choice_is_autofill;
|
| + defaults->SetString(kLastUsedAccountName, last_used_account_name);
|
| + defaults->SetBoolean(kLastUsedChoiceIsAutofill,
|
| + last_used_choice_is_autofill);
|
| + if (!last_used_billing.empty())
|
| + defaults->SetString(kLastUsedBillingAddressGuid, last_used_billing);
|
| + if (!last_used_shipping.empty())
|
| + defaults->SetString(kLastUsedShippingAddressGuid, last_used_shipping);
|
| + if (!last_used_card.empty())
|
| + defaults->SetString(kLastUsedCreditCardGuid, last_used_card);
|
| + } else {
|
| + LOG(ERROR) << "Failed to save AutofillDialog preferences";
|
| + }
|
| + }
|
| +
|
| + if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT) {
|
| + autocheckout_started_timestamp_ = base::Time::Now();
|
| + SetAutocheckoutState(AUTOCHECKOUT_IN_PROGRESS);
|
| + }
|
| +
|
| + LogOnFinishSubmitMetrics();
|
| +
|
| + // Callback should be called as late as possible.
|
| + callback_.Run(&form_structure_, google_transaction_id);
|
| +
|
| + // This might delete us.
|
| + if (GetDialogType() == DIALOG_TYPE_REQUEST_AUTOCOMPLETE)
|
| + Hide();
|
| +}
|
| +
|
| +AutofillDialogControllerAndroid::AutofillDialogControllerAndroid(
|
| + content::WebContents* contents,
|
| + const FormData& form_structure,
|
| + const GURL& source_url,
|
| + const DialogType dialog_type,
|
| + const base::Callback<void(const FormStructure*,
|
| + const std::string&)>& callback)
|
| + : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
|
| + contents_(contents),
|
| + initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
|
| + dialog_type_(dialog_type),
|
| + form_structure_(form_structure, std::string()),
|
| + invoked_from_same_origin_(true),
|
| + source_url_(source_url),
|
| + callback_(callback),
|
| + cares_about_shipping_(true),
|
| + weak_ptr_factory_(this),
|
| + autocheckout_state_(AUTOCHECKOUT_NOT_STARTED),
|
| + was_ui_latency_logged_(false) {
|
| + DCHECK(!callback_.is_null());
|
| +}
|
| +
|
| +bool AutofillDialogControllerAndroid::RequestingCreditCardInfo() const {
|
| + DCHECK_GT(form_structure_.field_count(), 0U);
|
| +
|
| + for (size_t i = 0; i < form_structure_.field_count(); ++i) {
|
| + AutofillType type = form_structure_.field(i)->Type();
|
| + if (common::IsCreditCardType(type.GetStorableType()))
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const {
|
| + return source_url_.SchemeIs(chrome::kHttpsScheme);
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::SetAutocheckoutState(
|
| + AutocheckoutState autocheckout_state) {
|
| + if (autocheckout_state_ == autocheckout_state)
|
| + return;
|
| +
|
| + autocheckout_state_ = autocheckout_state;
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::LogOnFinishSubmitMetrics() {
|
| + GetMetricLogger().LogDialogUiDuration(
|
| + base::Time::Now() - dialog_shown_timestamp_,
|
| + GetDialogType(),
|
| + AutofillMetrics::DIALOG_ACCEPTED);
|
| +
|
| + GetMetricLogger().LogDialogUiEvent(
|
| + GetDialogType(), AutofillMetrics::DIALOG_UI_ACCEPTED);
|
| +}
|
| +
|
| +void AutofillDialogControllerAndroid::LogOnCancelMetrics() {
|
| + GetMetricLogger().LogDialogUiDuration(
|
| + base::Time::Now() - dialog_shown_timestamp_,
|
| + GetDialogType(),
|
| + AutofillMetrics::DIALOG_CANCELED);
|
| +
|
| + GetMetricLogger().LogDialogUiEvent(
|
| + GetDialogType(), AutofillMetrics::DIALOG_UI_CANCELED);
|
| +}
|
| +
|
| +} // namespace autofill
|
|
|